最近在做一个Android盒子的项目,主要是Launcher有一个横向滚动的界面。主要使用的是RecyclerView。总结一下。

一、先了解下RecyclerView
RecyclerView是类似于ListView、GridView的一种AdapterView。相比较的优势是使用更加灵活,可以满足实现更多不同的效果。

在我要实现的水平滚动网格布局中就得到了很好的满足。因为使用HorizentalScrollView + GridView的模式会十分复杂,并且焦点、动作的监听会比较混乱。事件冲突处理起来特别麻烦。

二、实现简单例子
记录一下我自己学习的过程。先写了一下RecyclerViewTest的工程。这个工程的主要效果是在页面上显示Android设备上所安装的所有应用。并且点击应用图标可以进入相应的应用。点击菜单键可以卸载该应用。

大概就是这样:

1.先要关联recyclerview的jar包:

dependencies {    ...    compile 'com.android.support:recyclerview-v7:23.0.1'}

2.然后就可以使用RecyclerView了,先根据需求自定义了一个SimpleRecyclerView.java

public class SimpleRecycleView extends RecyclerView {    private static final String TAG = SimpleRecycleView.class.getSimpleName();    // 一个滚动对象    private Scroller mScroller;    private int mLastX = 0;    public SimpleRecycleView(Context context) {        super(context);        init(context);    }    public SimpleRecycleView(Context context, AttributeSet attrs) {        super(context, attrs);        init(context);    }    public SimpleRecycleView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init(context);    }    // 一个初始化方法,传入了一个上下文对象,用来初始化滚动对象    private void init(Context context){        mScroller = new Scroller(context);    }    // 重写了计算滚动方法    @Override    public void computeScroll() {        if(mScroller!=null && mScroller.computeScrollOffset()){            scrollBy(mLastX - mScroller.getCurrX(), 0);            mLastX = mScroller.getCurrX();            postInvalidate();        }    }    /**     * 调用此方法滚动到目标位置,其中(fx, fy)表示最终要滚到的目标位置的坐标值     * duration表示期间滚动的耗时。     *     * @param fx 目标位置的X向坐标值     * @param fy 目标位置的Y向坐标值     * @param duration 滚动到目标位置所消耗的时间毫秒值     */    @SuppressWarnings("unused")    public void smoothScrollTo(int fx, int fy,int duration) {        int dx = 0;        int dy = 0;        // 计算变化的位移量        if(fx != 0) {            dx = fx - mScroller.getFinalX();        }        if(fy!=0) {            dy = fy - mScroller.getFinalY();        }        Log.i(TAG, "fx:" + fx + ", getFinalX:" + mScroller.getFinalX() + ", dx:" + dx);        smoothScrollBy(dx, dy, duration);    }    /**     * 调用此方法设置滚动的相对偏移     */    public void smoothScrollBy(int dx, int dy, int duration) {        if(duration > 0) {            mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy, duration);        } else {            // 设置mScroller的滚动偏移量            mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);        }        // 重绘整个view,重绘过程会调用到computeScroll()方法。        // 这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果        invalidate();    }    /**     * 此方法用来检查自动调节     *     * @param position 要检查的位置     */    @SuppressWarnings("unused")    public void checkAutoAdjust(int position){        int childCount = getChildCount();        // 获取可视范围内的选项的头尾位置        int firstVisibleItemPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();        int lastVisibleItemPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();        Log.d(TAG, "childCount:" + childCount + ", position:" + position + ", firstVisibleItemPosition:" + firstVisibleItemPosition                + "  lastVisibleItemPosition:" + lastVisibleItemPosition);        if(position == (firstVisibleItemPosition + 1) || position == firstVisibleItemPosition){            // 当前位置需要向右平移            leftScrollBy(position, firstVisibleItemPosition);        } else if (position == (lastVisibleItemPosition - 1) || position == lastVisibleItemPosition){            // 当前位置需要向左平移            rightScrollBy(position, lastVisibleItemPosition);        }    }    private void leftScrollBy(int position, int firstVisibleItemPosition){        View leftChild = getChildAt(0);        if(leftChild != null){            int startLeft = leftChild.getLeft();            int endLeft = (position == firstVisibleItemPosition ? leftChild.getWidth() : 0);            Log.d(TAG, "startLeft:" + startLeft + " endLeft" + endLeft);            autoAdjustScroll(startLeft, endLeft);        }    }    private void rightScrollBy(int position, int lastVisibleItemPosition){        int childCount = getChildCount();        View rightChild = getChildAt(childCount - 1);        if(rightChild != null){            int startRight = rightChild.getRight() - getWidth();            int endRight = (position == lastVisibleItemPosition ? (-1 * rightChild.getWidth()) : 0);            Log.d(TAG,"startRight:" + startRight + " endRight:" + endRight);            autoAdjustScroll(startRight, endRight);        }    }    /**     *     * @param start 滑动起始位置     * @param end 滑动结束位置     */    private void autoAdjustScroll(int start, int end){        mLastX = start;        mScroller.startScroll(start, 0, end - start, 0);        postInvalidate();    }    /**     * 将指定item平滑移动到整个view的中间位置     * @param position 指定的item的位置     */    public void smoothScrollMaster(int position) {        // 这个方法是为了设置Scroller的滚动的,需要根据业务需求,编写算法。    }}

3.然后就可以在布局文件中使用自定义的控件了:

"http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@drawable/home_background">    <com.jiuzhou.porter.launcher.widget.SimpleRecycleView        android:id="@+id/home_apps"        android:layout_marginLeft="@dimen/px_positive_80"        android:layout_marginRight="@dimen/px_positive_80"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:scrollbars="none" />

我去不小心暴露了我的包名(@^_^@)

4.在MainActivity.java中编写代码:

//初始化RecyclerView,设置布局管理器、间距、适配器、数据等@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main)    ...    // 1.初始化SimpleRecyclerView    mRecyclerView = (SimpleRecycleView) findViewById(R.id.home_apps);    // 2.使用自定义的工具类获得设备中安装的APP    mListOfApps = LauncherCommonUtils.getAllApk(this);    // 3.初始化适配器    SimpleRecyclerAdapter mAdapter = new SimpleRecyclerAdapter(this, mListOfApps);    mRecyclerView.setItemAnimator(new DefaultItemAnimator());    // 4.设置布局管理器:瀑布流式    StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3 , StaggeredGridLayoutManager.HORIZONTAL);    // 5.根据需要设置间距等其他内容    mRecyclerView.setLayoutManager(staggeredGridLayoutManager);    int right = (int) getResources().getDimension(R.dimen.px_positive_5);    int bottom = (int) getResources().getDimension(R.dimen.px_positive_1);    RecyclerView.ItemDecoration spacingInPixel = new SpaceItemDecoration(right, bottom);    mRecyclerView.addItemDecoration(spacingInPixel);    // 6.关联适配器    mRecyclerView.setAdapter(mAdapter);}
  • 这里有必要说一下:
    关于布局管理器的内容:
    RecyclerView的使用时是必须设置布局管理器的。因为不同的布局管理器决定了展现出来的演示是怎样的。
    常见的集中布局管理器有LinearLayoutManager、RelativeLayoutManager、GridLayoutManager、StaggeredGridLayoutManager等。
    意思一目了然。只提一下StaggeredGridLayoutManager,这个是水平方向的Grid

这里比较重要的是Adapter
5. 关于SimpleRecyclerAdapter

/* *  Copyright (c) 2016.  Project Launcher *  Source SimpleRecyclerAdapter *  Author 沈煜 *  此源码及相关文档等附件由 沈煜 编写,作者保留所有权利 *  使用必须注明出处。 *  The code and documents is write by the author. All rights are reserved. *  Use must indicate the source. * */public class SimpleRecyclerAdapter extends RecyclerView.Adapter{    private static final String TAG = SimpleRecyclerAdapter.class.getSimpleName();    private LayoutInflater mInflater;    private List mListOfApps;    private int currentPosition = 0;    private Context context;    public SimpleRecyclerAdapter(Context context, List mListOfApps){        mInflater = LayoutInflater.from(context);        this.context = context;        this.mListOfApps = mListOfApps;    }    @SuppressWarnings("unused")    public void setData(List mListOfApps){        this.mListOfApps = mListOfApps;    }    @Override    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        View view = mInflater.inflate(R.layout.item_grid_apps, parent, false);        ViewHolder vh = new ViewHolder(view);        vh.mImageView = (ImageView) view.findViewById(R.id.home_grid_item_icon);        vh.mTextView = (TextView) view.findViewById(R.id.home_grid_item_name);        return vh;    }    private View mOldFocus;    @Override    public void onBindViewHolder(final ViewHolder holder, final int position) {        holder.mImageView.setImageDrawable(mListOfApps.get(position).getAppIcon());        holder.mTextView.setText(mListOfApps.get(position).getAppName());        // 设置itemView可以获得焦点        holder.itemView.setFocusable(true);        holder.itemView.setTag(position);        holder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {            @Override            public void onFocusChange(View v, boolean hasFocus) {                if (hasFocus) {                    currentPosition = (int) holder.itemView.getTag();                    mOnItemSelectListener.onItemSelect(holder.itemView, currentPosition);                    if (v != mOldFocus) {                        View vb = v.findViewById(R.id.home_back_2);                        GradientDrawable gd = (GradientDrawable) vb.getBackground();                        int width = (int) context.getResources().getDimension(R.dimen.px_positive_3);                        int color = context.getResources().getColor(R.color.color0);                        int radius = (int) context.getResources().getDimension(R.dimen.px_positive_25);                        gd.setStroke(width, color);                        gd.setCornerRadius(radius);                        if (mOldFocus != null) {                            View ovb = mOldFocus.findViewById(R.id.home_back_2);                            GradientDrawable ogd = (GradientDrawable) ovb.getBackground();                            ogd.setStroke(0, Color.parseColor("#00000000"));                        }                    }                    mOldFocus = v;                } else {                    if (v != null) {                        View ovb2 = v.findViewById(R.id.home_back_2);                        GradientDrawable ogd2 = (GradientDrawable) ovb2.getBackground();                        ogd2.setStroke(0, Color.parseColor("#00000000"));                    }                }            }        });        holder.itemView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                mOnItemClickListener.onItemClick(v, currentPosition);            }        });        holder.itemView.setOnKeyListener(new View.OnKeyListener() {            @Override            public boolean onKey(View v, int keyCode, KeyEvent event) {                mOnItemKeyListener.OnItemKey(v, keyCode, event, currentPosition);                return false;            }        });    }    @Override    public int getItemCount() {        return mListOfApps.size();    }    private int index = 0;    class ViewHolder extends RecyclerView.ViewHolder{        ImageView mImageView;        TextView mTextView;        ViewHolder(View itemView) {            super(itemView);            ImageView back2 = (ImageView) itemView.findViewById(R.id.home_back_2);            GradientDrawable background = (GradientDrawable) back2.getBackground();            TypedArray ta = context.getResources().obtainTypedArray(R.array.appBackgroundColors);            int count = ta.length();            int [] colorsArray = new int[count];            for (int i=0;iint resId = ta.getResourceId(i, -1);                colorsArray[i] = resId;            }            /*Random random = new Random();            int index = random.nextInt(count);            while (oldIndex == index) {                index = random.nextInt();            }            oldIndex = index;*/            background.setColor(context.getResources().getColor(colorsArray[index]));            if (index < count - 1) {                index += 1;            } else {                index = 0;            }            ta.recycle();        }    }    private OnItemSelectListener mOnItemSelectListener;    private OnItemClickListener mOnItemClickListener;    private OnItemLongClickListener mOnItemLongClickListener;    private OnItemKeyListener mOnItemKeyListener;    public interface OnItemSelectListener {        void onItemSelect(View view, int position);    }    public interface OnItemClickListener {        void onItemClick(View view, int position);    }    public interface OnItemLongClickListener {        void onItemLongClick(View view, int position);    }    public interface OnItemKeyListener {        void OnItemKey(View view, int keyCode, KeyEvent event, int position);    }    public void setOnItemSelectListener(OnItemSelectListener listener){        mOnItemSelectListener = listener;    }    public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) {        this.mOnItemClickListener = mOnItemClickListener;    }    public void setOnItemLongClickListener(OnItemLongClickListener mOnItemLongClickListener) {        this.mOnItemLongClickListener = mOnItemLongClickListener;    }    public void setOnItemKeyListener(OnItemKeyListener mOnItemKeyListener) {        this.mOnItemKeyListener = mOnItemKeyListener;    }}

最后有一个重要的地方就是Adapter中写了许多监听器。这个是RecyclerView特有的,因为RecyclerView没有监听器!!!(简直了,不能忍好嘛。所以要在Adapter中自己定义监听。因为你监听的是其中的itemView,当然了也可以去RecyclerView里面写诸如OnItemClick这样的监听器,会麻烦一点。不过那样封装起来比较牛。去GitHub上应该有这样的jar可以用。)

差不多了吧

————————-不怎么华丽的分割线—————–

MDZZ,这个里面的焦点控制忘了写!!!厉害了我的哥,下期详解。

更多相关文章

  1. 第17天 Android(安卓)Touch事件学习 4 获取手指触摸位置
  2. android LinearLayout布局嵌套覆盖问题
  3. 修正AppCompatSpinner弹出框位置问题
  4. Android桌面组件App Widget开发三步走
  5. Android学习整理 -1- Fragment 学习
  6. Android(安卓)实现自定义的卫星式菜单(弧形菜单)View
  7. Android(安卓)SDK Manager无法显示可供下载的未安装SDK解决方案
  8. Android(安卓)Layout 布局
  9. Android(Xamarin)之旅(三)

随机推荐

  1. Android(安卓)Handler 四个使用实例 及Ha
  2. OSG for Android新手教程系列(二)——项目
  3. Android进阶:十二、最简单的方式实现自定
  4. Android应用优化(5)几种内存泄露和解决办法
  5. 关于android创建快捷方式会启动两个应用
  6. 仅需6步,教你轻易撕掉app开发框架的神秘面
  7. java Android(安卓)回调机制的详解
  8. Android(安卓)Add外部library工程,总是链
  9. TouchPad 可以跑 Android,eBay 上就買得到
  10. Android中直播视频技术探究之---基础知识