当前位置:   article > 正文

Android TV横向滚动网格布局——RecyclerView的使用_recyclerview网格横向

recyclerview网格横向

最近在做一个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'
}
  • 1
  • 2
  • 3
  • 4

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的滚动的,需要根据业务需求,编写算法。
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140

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

<RelativeLayout xmlns:android="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" />
</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

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

//初始化RecyclerView,设置布局管理器、间距、适配器、数据等
@Override
protected 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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 这里有必要说一下:
    关于布局管理器的内容:
    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<SimpleRecyclerAdapter.ViewHolder>{
    private static final String TAG = SimpleRecyclerAdapter.class.getSimpleName();
    private LayoutInflater mInflater;
    private List<AppBean> mListOfApps;
    private int currentPosition = 0;
    private Context context;

    public SimpleRecyclerAdapter(Context context, List<AppBean> mListOfApps){
        mInflater = LayoutInflater.from(context);
        this.context = context;
        this.mListOfApps = mListOfApps;
    }

    @SuppressWarnings("unused")
    public void setData(List<AppBean> 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;i<count;i++) {
                int 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;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172

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

差不多了吧

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/174703?site
推荐阅读
相关标签
  

闽ICP备14008679号