本文共 12316 字,大约阅读时间需要 41 分钟。
本文主要介绍Android Support v7提供的RecycleView和交错式布局(通常成为瀑布流布局)的使用和事件监听处理。
1. 涉及到开源库有:
:
Facebook开源的不是一般强大的图片加载组件库
:
Android View和事件绑定库,通过注解完成,在编译时APT处理注解文档。
2. 模块配置
Android Studio模块,build.gradle配置:
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 | apply plugin: 'com.android.application' android { compileSdkVersion 21 buildToolsVersion '21.1.2' defaultConfig { applicationId 'secondriver.sdk' minSdkVersion 16 targetSdkVersion 21 versionCode 1 versionName '1.0' } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile( 'proguard-android.txt' ), 'proguard-rules.pro' } } packagingOptions { exclude 'META-INF/services/javax.annotation.processing.Processor' } lintOptions { disable 'InvalidPackage' } } dependencies { compile fileTree(dir: 'libs' , include: [ '*.jar' ]) compile 'com.android.support:appcompat-v7:21.0.3' compile 'com.android.support:recyclerview-v7:21.0.3@aar' compile 'com.android.support:cardview-v7:21.0.3@aar' compile 'com.facebook.fresco:fresco:0.8.0' compile 'com.jakewharton:butterknife:7.0.1' testCompile 'junit:junit:4.12' } |
3. 布局文件
主布局文件:activity_recycleview.xml 包含一个RecycleView,将作为主屏幕组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 | <? xml version = "1.0" encoding = "utf-8" ?> < RelativeLayout android:id = "@+id/swipe_refresh_layout" xmlns:android = "http://schemas.android.com/apk/res/android" android:layout_width = "match_parent" android:layout_height = "wrap_content" > < android.support.v7.widget.RecyclerView android:id = "@+id/recycler_view" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_alignParentTop = "true" android:scrollbars = "vertical" /> </ RelativeLayout > |
RecycleView 通过设置不同的布局管理器对象来实现不同的布局显示,如:
android.support.v7.widget.LinearLayoutManager 可以实现ListView的布局效果
android.support.v7.widget.GridLayoutManager 可以实现GridView的布局效果
android.support.v7.widget.StaggeredGridLayoutManager 可以实现交错式网格布局效果
次布局文件(RecycleView中显示的每一项视图的布局):item_recycelview.xml 通过com.android.support:CardView包提供的CardView帧式容器布局包含一个ImageView和TextView作为RecycleView的每一项视图的布局。
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 | <? xml version = "1.0" encoding = "utf-8" ?> < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:card_view = "http://schemas.android.com/apk/res-auto" xmlns:fresco = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" android:gravity = "center" > < android.support.v7.widget.CardView android:id = "@+id/card_view" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_gravity = "center" card_view:cardCornerRadius = "4dp" card_view:cardUseCompatPadding = "true" > < RelativeLayout android:layout_width = "match_parent" android:layout_height = "wrap_content" android:orientation = "vertical" > < com.facebook.drawee.view.SimpleDraweeView android:id = "@+id/info_image" android:layout_width = "300dp" android:layout_height = "420dp" fresco:actualImageScaleType = "centerCrop" fresco:placeholderImage = "@mipmap/ic_launcher" fresco:roundAsCircle = "false" fresco:roundBottomLeft = "true" fresco:roundBottomRight = "true" fresco:roundTopLeft = "true" fresco:roundTopRight = "true" fresco:roundedCornerRadius = "1dp" fresco:roundingBorderWidth = "2dp" /> < TextView android:id = "@+id/info_text" android:layout_width = "200dp" android:layout_height = "80dp" android:textSize = "20sp" android:textColor = "@android:color/holo_blue_dark" android:textAppearance = "?android:attr/textAppearanceMedium" android:layout_alignLeft = "@id/info_image" android:layout_alignRight = "@id/info_image" android:layout_below = "@id/info_image" android:gravity = "left|center_vertical" /> </ RelativeLayout > </ android.support.v7.widget.CardView > </ LinearLayout > |
ImageView组件使用的是Fresco库提供的视图组件。
4. Activity实现和数据填充
4.1 RecycleView中的每一项视图的数据组成
文本信息+ 图片地址(url,file,resId) 本示例中采用Url网络图片
1 2 3 4 5 6 7 8 9 10 11 12 | /** * View项的数据对象 */ static class Pair { public String text; public String url; public Pair(String text, String url) { this .text = text; this .url = url; } } |
4.2 RecycleView中国的每一项视图的数据填充,即适配器
自定义适配器需要实现RecycleView.Adapter类。
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 | static class RecycleViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { /** * RecycleView的View项单击事件监听 */ public interface OnRecycleViewItemClickListener { void onRecycleViewItemClick(View view, int position); } private ArrayList<Pair> items = new ArrayList<>(); private OnRecycleViewItemClickListener mOnRecycleViewItemClickListener; public RecycleViewAdapter(ArrayList<Pair> items) { this .items = items; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyclerview, parent, false ); return new RecycleViewItemHolder(view, mOnRecycleViewItemClickListener); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { Pair pair = items.get(position); ((RecycleViewItemHolder) holder).setContent(pair); } @Override public int getItemCount() { return items.size(); } public void setOnRecycleViewItemClickListener(OnRecycleViewItemClickListener onItemClickListener) { if ( null != onItemClickListener) { this .mOnRecycleViewItemClickListener = onItemClickListener; } } } |
主要实现onCreateViewHolder和onBindViewHolder方法。由于RecycleView并未提供为视图项添加监听事件,这里自定义个一个事件监听接口,在实例化适配器的时候可以选择设置事件监听。
4.3 创建ViewHolder需要通过自定义ViewHolder类继承RecycleView.ViewHolder。
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 | static class RecycleViewItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener { @Bind (R.id.info_text) public TextView infoTextView; @Bind (R.id.info_image) public SimpleDraweeView draweeView; private RecycleViewAdapter.OnRecycleViewItemClickListener onItemClickListener; public RecycleViewItemHolder(View itemView, RecycleViewAdapter.OnRecycleViewItemClickListener onItemClickListener) { super (itemView); ButterKnife.bind( this , itemView); this .onItemClickListener = onItemClickListener; itemView.setOnClickListener( this ); } public void setContent(Pair pair) { infoTextView.setText(pair.text); draweeView.setImageURI(Uri.parse(pair.url)); } @Override public void onClick(View v) { if ( null != onItemClickListener) { onItemClickListener.onRecycleViewItemClick(v, getPosition()); } } } |
在构造方法中的传入OnRecycleViewItemClickListener对象,并且自定义ViewHolder实现OnClickListener接口,通过为itemView对象设置OnClickListener监听事件,在onClick方法中将点击事件的处理交由OnRecycleViewItemClickListener对象处理,从而达到为RecycleView中的itemView注册点击事件。
如果不采用这种方式添加事件监听,就不需要自定义监听接口和自定义ViewHolder实现事件监听接口以及事件处理。另外可以通过获得的itemView之后,通过组件I的获取组件来添加事件监听。还可以通过获得的自定义ViewHolder,来访问每个组件。
4.4 Activity的具体实现
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 | package secondriver.sdk.activity; import android.app.Activity; import android.net.Uri; import android.os.Bundle; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; import com.facebook.drawee.view.SimpleDraweeView; import java.util.ArrayList; import java.util.Arrays; import java.util.Random; import butterknife.Bind; import butterknife.ButterKnife; import secondriver.sdk.R; /** * Author : secondriver * Created : 2015/11/18 */ public class RecycleViewActivity extends Activity { private static final String TAG = RecyclerView. class .getName(); private static final String[] RES_URL = new String[]{ "http://p1.wmpic.me/article/2015/11/16/1447644849_hySANEEF.jpg" , //减少篇幅,此处省去14个图片Url }; @Bind (R.id.recycler_view) public RecyclerView mRecycleView; private final int PRE_SCREEN_NUMBER = 6 ; private final int SPAN_COUNT = 2 ; private int previousLastIndex = 0 ; private boolean isSlidingToLast = false ; private RecycleViewAdapter mAdapter; private ArrayList<Pair> mItem = new ArrayList<>(); //交错式网格布局管理对象,即通常称的瀑布流布局 private StaggeredGridLayoutManager mLayoutManager; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_recyclerview); ButterKnife.bind( this ); Fresco.initialize( this ); //重要,Fresco做一系列初始化工作 initRecycleView(); } private void initRecycleView() { mLayoutManager = new StaggeredGridLayoutManager(SPAN_COUNT, StaggeredGridLayoutManager.VERTICAL); mRecycleView.setLayoutManager(mLayoutManager); mAdapter = new RecycleViewAdapter(mItem); loadData( false ); mRecycleView.setAdapter(mAdapter); mRecycleView.setItemAnimator( new DefaultItemAnimator()); //RecycleView的View项单击事件监听 mAdapter.setOnRecycleViewItemClickListener( new RecycleViewAdapter.OnRecycleViewItemClickListener() { @Override public void onRecycleViewItemClick(View view, int position) { long id = mRecycleView.getChildItemId(view); Log.d(TAG, "View项的根视图:" + view.getClass().getName() + ",position=" + position + " ViewHolder_Id=" + id); //通过findViewById查找View项中的元素 SimpleDraweeView draweeView = (SimpleDraweeView) view.findViewById(R.id.info_image); if ( null != draweeView) { draweeView.setImageURI(Uri.parse(RES_URL[ 0 ])); Toast.makeText(RecycleViewActivity. this , "通过findViewById查找View项中的元素" , Toast.LENGTH_LONG).show(); } RecycleViewItemHolder recycleViewItemHolder = (RecycleViewItemHolder) mRecycleView.findViewHolderForPosition(position); if ( null != recycleViewItemHolder) { recycleViewItemHolder.infoTextView.setText( "通过ViewHolder找到View项中的元素" ); } } }); //下拉刷新,追加内容 mRecycleView.setOnScrollListener( new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super .onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE) { if (isPullToBottom() && isSlidingToLast) { if (mItem.size() > 36 ) { //最大数据量 Toast.makeText(RecycleViewActivity. this , "没有数据了" , Toast.LENGTH_LONG).show(); return ; } else { loadData( false ); Log.d(TAG, "notifyItemRangeInserted startPosition=" + previousLastIndex); mAdapter.notifyItemRangeInserted(previousLastIndex, PRE_SCREEN_NUMBER); } } } if (newState == RecyclerView.SCROLL_STATE_SETTLING) { Log.d(TAG, "settling" ); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super .onScrolled(recyclerView, dx, dy); isSlidingToLast = dy > 0 ; //上拉,下滑 Log.d(TAG, "dx = " + dx + " dy=" + dy + " isSlidingToLast=" + isSlidingToLast); } } ); } private boolean isPullToBottom() { int [] lastIndexs = mLayoutManager.findLastCompletelyVisibleItemPositions( null ); Log.d(TAG, "last item =" + Arrays.toString(lastIndexs) + ", have item=" + mAdapter.getItemCount()); int maxIndex = mAdapter.getItemCount() - 1 ; for ( int i : lastIndexs) { if (i == maxIndex) { return true ; } } return false ; } private void loadData( boolean isClear) { if (isClear) { mItem.clear(); } previousLastIndex = mItem.size(); Random r = new Random(); for ( int index = 0 ; index < PRE_SCREEN_NUMBER && index < RES_URL.length; index++) { mItem.add( new Pair( "Card " + (previousLastIndex + index), RES_URL[r.nextInt(RES_URL.length)])); } Log.d(TAG, "mItem count =" + mItem.size()); } } |
说明:
RecycleView的ItemView的数据由Pair对象管理存储
Fresco库默认配置下的使用之前需要初始化调用Fresco.initialize(Context)
上述实现了2个事件监听,一个OnScrollerListener由RecycleView提供,实现下拉刷新;一个OnRecycleViewItemClickListener由自定义的RecycleViewAdpater提供,实现单击itemView来更换图片(SimpleDraweeView)和文字(TextView)。
和库的具体使用参见其Github上的文档。
5. 效果图
比较遗憾ScreenRecord不支持模拟器和Android4.4一下的系统,只能附上截图。