[Android学习笔记三] Support v7提供交错式网格布局开发示例
本文主要介绍Android Support v7提供的RecycleView和交错式布局(通常成为瀑布流布局)的使用和事件监听处理。 1. 涉及到开源库有: Fresco: Facebook开源的不是一般强大的图片加载组件库 Bufferknife: 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 applyplugin: '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{ minifyEnabledfalse proguardFilesgetDefaultProguardFile( 'proguard-android.txt' ), 'proguard-rules.pro' } } packagingOptions{ exclude 'META-INF/services/javax.annotation.processing.Processor' } lintOptions{ disable 'InvalidPackage' } } dependencies{ compilefileTree(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 Stringtext; public Stringurl; public Pair(Stringtext,Stringurl){ 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(Viewview, int position); } private ArrayList<Pair>items= new ArrayList<>(); private OnRecycleViewItemClickListenermOnRecycleViewItemClickListener; public RecycleViewAdapter(ArrayList<Pair>items){ this .items=items; } @Override public RecyclerView.ViewHolderonCreateViewHolder(ViewGroupparent, int viewType){ Viewview=LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyclerview,parent, false ); return new RecycleViewItemHolder(view,mOnRecycleViewItemClickListener); } @Override public void onBindViewHolder(RecyclerView.ViewHolderholder, int position){ Pairpair=items.get(position); ((RecycleViewItemHolder)holder).setContent(pair); } @Override public int getItemCount(){ return items.size(); } public void setOnRecycleViewItemClickListener(OnRecycleViewItemClickListeneronItemClickListener){ 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 TextViewinfoTextView; @Bind (R.id.info_image) public SimpleDraweeViewdraweeView; private RecycleViewAdapter.OnRecycleViewItemClickListeneronItemClickListener; public RecycleViewItemHolder(ViewitemView,RecycleViewAdapter.OnRecycleViewItemClickListeneronItemClickListener){ super (itemView); ButterKnife.bind( this ,itemView); this .onItemClickListener=onItemClickListener; itemView.setOnClickListener( this ); } public void setContent(Pairpair){ infoTextView.setText(pair.text); draweeView.setImageURI(Uri.parse(pair.url)); } @Override public void onClick(Viewv){ 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 StringTAG=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 RecyclerViewmRecycleView; private final int PRE_SCREEN_NUMBER= 6 ; private final int SPAN_COUNT= 2 ; private int previousLastIndex= 0 ; private boolean isSlidingToLast= false ; private RecycleViewAdaptermAdapter; private ArrayList<Pair>mItem= new ArrayList<>(); //交错式网格布局管理对象,即通常称的瀑布流布局 private StaggeredGridLayoutManagermLayoutManager; @Override protected void onCreate(BundlesavedInstanceState){ 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(Viewview, int position){ long id=mRecycleView.getChildItemId(view); Log.d(TAG, "View项的根视图:" +view.getClass().getName()+ ",position=" +position+ "ViewHolder_Id=" +id); //通过findViewById查找View项中的元素 SimpleDraweeViewdraweeView=(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(); } RecycleViewItemHolderrecycleViewItemHolder=(RecycleViewItemHolder)mRecycleView.findViewHolderForPosition(position); if ( null !=recycleViewItemHolder){ recycleViewItemHolder.infoTextView.setText( "通过ViewHolder找到View项中的元素" ); } } }); //下拉刷新,追加内容 mRecycleView.setOnScrollListener( new RecyclerView.OnScrollListener(){ @Override public void onScrollStateChanged(RecyclerViewrecyclerView, 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, "notifyItemRangeInsertedstartPosition=" +previousLastIndex); mAdapter.notifyItemRangeInserted(previousLastIndex,PRE_SCREEN_NUMBER); } } } if (newState==RecyclerView.SCROLL_STATE_SETTLING){ Log.d(TAG, "settling" ); } } @Override public void onScrolled(RecyclerViewrecyclerView, 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, "lastitem=" +Arrays.toString(lastIndexs)+ ",haveitem=" +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(); Randomr= 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, "mItemcount=" +mItem.size()); } } 说明: RecycleView的ItemView的数据由Pair对象管理存储 Fresco库默认配置下的使用之前需要初始化调用Fresco.initialize(Context) 上述实现了2个事件监听,一个OnScrollerListener由RecycleView提供,实现下拉刷新;一个OnRecycleViewItemClickListener由自定义的RecycleViewAdpater提供,实现单击itemView来更换图片(SimpleDraweeView)和文字(TextView)。 Butterknife和Fresco库的具体使用参见其Github上的文档。 5. 效果图 比较遗憾ScreenRecord不支持模拟器和Android4.4一下的系统,只能附上截图。 本文转自 secondriver 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1715139,如需转载请自行联系原作者