[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
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)。

    ButterknifeFresco库的具体使用参见其Github上的文档。

 

5. 效果图

  

  比较遗憾ScreenRecord不支持模拟器和Android4.4一下的系统,只能附上截图。

  

wKiom1ZO4o-QeQpgAAGxEVl9mcg721.pngwKiom1ZO4pHTryCOAAFsNVfJcQo288.png

wKiom1ZO4pXhJ94JAAHzXksmYI8284.pngwKiom1ZO4pjjCzOTAAHD0AdMo8o213.png




本文转自 secondriver 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1715139,如需转载请自行联系原作者
相关文章
|
3月前
|
移动开发 前端开发 Android开发
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
353 12
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
3月前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
353 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
3月前
|
移动开发 Rust JavaScript
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
799 4
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
4月前
|
开发工具 Android开发
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
618 11
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
|
4月前
|
存储 消息中间件 人工智能
【05】AI辅助编程完整的安卓二次商业实战-消息页面媒体对象(Media Object)布局实战调整-按钮样式调整实践-优雅草伊凡
【05】AI辅助编程完整的安卓二次商业实战-消息页面媒体对象(Media Object)布局实战调整-按钮样式调整实践-优雅草伊凡
151 11
【05】AI辅助编程完整的安卓二次商业实战-消息页面媒体对象(Media Object)布局实战调整-按钮样式调整实践-优雅草伊凡
|
3月前
|
移动开发 Android开发
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
205 0
|
4月前
|
XML 存储 Java
【06】AI辅助编程完整的安卓二次商业实战-背景布局变更增加背景-二开发现页面跳转逻辑-替换剩余图标-优雅草卓伊凡
【06】AI辅助编程完整的安卓二次商业实战-背景布局变更增加背景-二开发现页面跳转逻辑-替换剩余图标-优雅草卓伊凡
130 3
【06】AI辅助编程完整的安卓二次商业实战-背景布局变更增加背景-二开发现页面跳转逻辑-替换剩余图标-优雅草卓伊凡
|
4月前
|
Java 开发工具 Maven
【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
287 6
|
存储 Java 开发工具
Android开发的技术与开发流程
Android开发的技术与开发流程
634 1
|
安全 Android开发 Swift
安卓与iOS开发:平台差异与技术选择
【8月更文挑战第26天】 在移动应用开发的广阔天地中,安卓和iOS两大平台各占一方。本文旨在探索这两个系统在开发过程中的不同之处,并分析开发者如何根据项目需求选择合适的技术栈。通过深入浅出的对比,我们将揭示各自平台的优势与挑战,帮助开发者做出更明智的决策。
271 5