安卓之Gallery实现刷新加载和分页加载

来源:互联网 发布:php有java难吗 编辑:程序博客网 时间:2024/05/20 18:18

   在项目的进展中使用了Gallery控件展示相册,由于Gallery控件已经被谷歌弃用,所以建议大家考虑使用新的控件来代替,及时跟上潮流。不过本篇的重点不是讨论被弃用的Gallery控件,而是来讨论怎么使用Gallery控件实现类似ListView控件的刷新和加载功能,因为项目里面使用的是PullToRefresh的库,但是该库好像并不支持Gallery的刷新和加载功能,所以只得自己参考别人的写法来实现该功能了。本文提供一种实现思路,希望可以帮到大家。(源码附在文后,欢迎大家下载,不要分!!)

  先看下效果图(用的是在线GIF生成工具):

 

下面开始正文。首先讲一下实现的思路:Gallery是横向滑动的控件,我们类似ListView实现刷新一样,当滑动到第0项时候,开始刷新操作,滑动到最后一项的时候进行分页加载的操作

顺着这个思路,我们开始编码了:首先是一头一尾两个界面,刷新界面和加载更多的界面,两个界面布局类似,就只贴一个布局文件了。

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical" android:layout_width="match_parent"    android:layout_height="match_parent">    <ProgressBar        style="?android:attr/progressBarStyleLarge"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentTop="true"        android:layout_centerHorizontal="true"        android:layout_marginTop="124dp"        android:id="@+id/progressBarLoadMore" />    <TextView        android:text="@string/loadMore"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerVertical="true"        android:layout_below="@+id/progressBarLoadMore"        android:layout_marginTop="30dp"        android:textSize="24sp"        android:layout_centerHorizontal="true"        android:id="@+id/textView2" /></RelativeLayout>

写完之后大概长这个样子,当然你可以根据自己的喜好去定制


     原来的Gallery控件肯定不能实现我们想要的效果,所以下面我们来定制自己的Gallery,对原来的Galley稍稍动一些手脚改造一下。接着上代码:

public class MyGallery extends Gallery implements android.widget.AdapterView.OnItemSelectedListener{    private IGalleryEventListener mIGalleryEventListener; // 监听Gallery滑动    private View refreshView ; // 刷新的缓冲界面    private View loadMoreView ; // 加载更多缓冲的界面    private int startIndex = 0 ; // 记录首项的索引    private int endIndex = 0 ; // 记录最后一项的索引    public MyGallery(Context context) {        super(context);        this.setOnItemSelectedListener(this); // 设置监听器    }    public MyGallery(Context context, AttributeSet attrs) {        super(context, attrs);        this.setOnItemSelectedListener(this); // 设置监听器    }    public MyGallery(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        this.setOnItemSelectedListener(this); // 设置监听器    }    /**     * 对外提供设置监听器的方法     * @param iGalleryEventListener Gallery的滑动监听器     */    public void setGalleryEventListerner(IGalleryEventListener iGalleryEventListener){        this.mIGalleryEventListener = iGalleryEventListener;    }    /**     * 刷新或加载结束的回调方法     */    public void onCompleted(){        if(refreshView != null && refreshView.isShown()){            refreshView.setVisibility(View.GONE); // 刷新页面不可见            this.setSelection(startIndex+1); // 选中刷新页面的后一项        }        if(loadMoreView != null && loadMoreView.isShown()){            loadMoreView.setVisibility(View.GONE); // 加载更多页面不可见            if(this.getChildAt(endIndex) != null ) this.setSelection(endIndex) ; // 有数据就选中刚刚加载的一项            else this.setSelection(endIndex-1); // 否则就选中加载更多页面的前面一项        }    }    @Override    public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {        startIndex = 0 ; // 首项        endIndex = adapterView.getCount() - 1 ; // 最后一项        if(position == startIndex ){ // 表示滑动到了最前面一页,刷新操作            refreshView = view ; // 刷新页面保存            if(!refreshView.isShown()) refreshView.setVisibility(View.VISIBLE);            mIGalleryEventListener.onRefresh(); // 回调刷新的方法        }        if(position == endIndex){ // 表示滑动到了最后一页,加载操作            loadMoreView = view ;            if(!loadMoreView.isShown()) loadMoreView.setVisibility(View.VISIBLE);            mIGalleryEventListener.onLoadMore(); // 回调加载的方法        }    }    @Override    public void onNothingSelected(AdapterView<?> adapterView) {    }}

注释已经写得很清楚了,红色部分是我们自己定义的一个接口,有两个待实现的方法(加载更多和刷新),红色部分的方法给当前的Gallery提供设置这个接口的方法,后面要用到。蓝色部分是结束时候的回调方法,紫色部分是当前的Gallery实现了OnItemSelectedListener接口的一些处理,当滑动到最前面一页的时候让刷新界面出现,当滑动到最后一页的时候让加载更多的界面出现

public interface IGalleryEventListener {    public void onLoadMore(); // 加载更多的回调函数    public void onRefresh(); // 刷新的回调函数}
上面是监听的接口,包含两个待实现的方法,后面再要使用的地方实现该接口即可。下面写一下Gallery的适配器

public class MyGalleryAdapter extends BaseAdapter {    private Context mContext ; // 运行的上下文    private List<PictureBean> mPictureList ; // 实体类数据集合    private LayoutInflater mInfalter ; //  界面渲染器    private View refreshView, loadMoreView; // 一头一尾    public MyGalleryAdapter(){    }    /**     * 构造方法     * @param context 上下文     * @param pictureList  实体类数据集合     */    public MyGalleryAdapter(Context context , List<PictureBean> pictureList){        this.mContext = context ;        this.mPictureList = pictureList ;    }    @Override    public int getCount() {        return mPictureList.size() + 2; // 这里多加了两个是因为我们多加了两个界面,分别是加载和刷新的界面,在数据集合中一头一尾    }    @Override    public PictureBean getItem(int position) {        if(position==0 || position== getCount()-1) return null;        return mPictureList.get(position-1) ;    }    @Override    public long getItemId(int i) {        return i;    }    @Override    public View getView(int position, View view, ViewGroup viewGroup) {        mInfalter = LayoutInflater.from(mContext);        if(position == 0 ){ // 第一页            refreshView = mInfalter.inflate(R.layout.refresh_layout ,null);            refreshView.setVisibility(View.GONE); // 默认设置刷新页面不可见            return refreshView; // 返回刷新页面        }else if(position == this.getCount() - 1){ //  最后一页            loadMoreView = mInfalter.inflate(R.layout.loadmore_layout ,null);            loadMoreView.setVisibility(View.GONE); // 默认设置加载更多不可见            return loadMoreView; // 返回加载更多页面        }else{            if(view == null) view = mInfalter.inflate(R.layout.content_layout, null);            ImageView imageView = ViewHolder.get(view,R.id.image);            TextView describeTv = ViewHolder.get(view,R.id.describeTv);            imageView.setImageResource(getItem(position).getPictureResId()); // 设置相片资源ID            describeTv.setText(getItem(position).getPictureName());  // 设置相片名称            return view ; // 返回内容页面        }    }}


上面代码注意红色加粗的部分,在原来的数据集合长度上在加上2,表示一头一尾各加了一个布局占位。

蓝色部分也很好理解,初始的时候让一头一尾的两个界面都隐藏起来,该显示的时候才让其显示。

这里有一个R.layout.content_layout的布局,如下,包含一个ImageView和一个TextView

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:padding="10dp"    android:gravity="center">    <TextView        android:id="@+id/describeTv"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_below="@+id/image"        android:layout_centerHorizontal="true"        android:layout_marginTop="20dp"        android:text="TextView"        android:textSize="24sp" />    <ImageView        android:id="@+id/image"        android:layout_width="300dp"        android:layout_height="360dp"        android:src="@mipmap/ic_launcher"        android:layout_centerInParent="true" /></RelativeLayout>

下面开始看MainActivity的布局界面

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.cjt.galleryrefreshdemo.MainActivity">    <com.cjt.galleryrefreshdemo.view.MyGallery        android:id="@+id/myGallery"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:spacing="15dp"        android:layout_alignParentTop="true"        android:layout_alignParentRight="true"        android:layout_alignParentEnd="true"        android:layout_marginRight="17dp"        android:layout_marginEnd="17dp">    </com.cjt.galleryrefreshdemo.view.MyGallery></RelativeLayout>

这里使用的是我们刚刚自定义的Gallery布局,那么MainActivity要使用刷新和加载的功能,就不得不实现我们刚刚定义的接口了,代码如下

public class MainActivity extends AppCompatActivity implements IGalleryEventListener{    private MyGallery myGallery ; // 自定义Gallery控件    private MyGalleryAdapter myGalleryAdapter ; // 适配器    private List<PictureBean> pictureList = new ArrayList<>();  // 数据集合,使用前要先转换为List的子类    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        myGallery = (MyGallery) findViewById(R.id.myGallery); /// 获取界面自定义的Gallery控件        myGallery.setGalleryEventListerner(this); // 为Gallery设置滑动监听器        // 准备数据        PictureBean bean ;        for (int i = 1; i <= 7 ; i++) {            bean = new PictureBean();            // 下面一句是获取mipmap中对应的图片名称的资源ID            int picResId = this.getResources().getIdentifier("pic"+i,"mipmap",getPackageName());            bean.setPictureResId(picResId);            bean.setPictureName("照片--"+i);            pictureList.add(bean);        }        // 设置适配器        myGalleryAdapter = new MyGalleryAdapter(this,pictureList) ;        myGallery.setAdapter(myGalleryAdapter);        myGallery.setSelection(1);   // 默认选中第一项,跳过第0项    }    @Override    public void onLoadMore() {        // 在这里处理加载更多的事件        taskThread(0);    }    @Override    public void onRefresh() {       // 在这里处理刷新的事件        taskThread(1);    }    /**     * 一般新开一个线程用于加载或刷新操作     * @param type  这里的type用于区分是刷新还是加载操作     */    private void taskThread(final int type){        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(2000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                Log.e("CJT","这里可以新建加载或刷新的任务");                getLoad(type); // 这里是模拟加载更多的任务            }        }).start();    }    // 模拟获取数据的方法,这里一般是网络请求加载数据    private void getLoad(int type){        if(type == 0){ // 加载操作            mHandler.sendEmptyMessage(0x01); // 加载完毕之后,发送消息,更新界面            // 这里模拟加载更多数据            // 准备数据            PictureBean bean ;            for (int i = 1; i <= 4 ; i++) {                bean = new PictureBean();                int picResId = this.getResources().getIdentifier("m"+i,"mipmap",getPackageName());                bean.setPictureResId(picResId);                bean.setPictureName("美女--"+i);                pictureList.add(bean);            }        }else{            mHandler.sendEmptyMessage(0x02); // 刷新完毕之后,发送消息,更新界面        }    }    // 一般使用Handler来更新界面    private Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            if(msg.what == 0x01){                // 更新界面                if(myGalleryAdapter != null )                    myGalleryAdapter.notifyDataSetChanged();            }else  if(msg.what == 0x02){                // 更新界面            }            myGallery.onCompleted(); // 同时调用加载结束的回调函数,停止转圈圈        }    };}

首先实现了我们自定义的接口,并且重写了两个方法(刷新和加载),在刷新和加载中我们开启了一个新的线程,模拟在后台实现的网络请求过程,加入线程休眠Thread.sleep(2000)主要是让刷新界面或者加载界面都能出来露个脸,就是让圈圈出来转一下。getLoad()方法就是模拟我们的后台请求数据,我这里使用一个int类型用来区分是刷新请求还是加载更多的请求,当然你们可以根据自己的需要去增加相应的请求的参数,比如分页加载需要页码(pageNum)等。推荐使用Handler来更新界面,如果是加载更多的话,只需要调用Adapter的notifyDataSetChanged()方法,如果是刷新的话根据业务需求来更新,比如我是将数据集合清空,然后在填充一次,这样避免了数据重复。最后别忘了回调加载结束的方法,不然那个刷新或加载更多的圈圈会一直不停的转、不停的转、不停的转……。

这里有一个测试用的实体类,也一并贴上来,显得文章长一些…………

public class PictureBean {    private int pictureResId ; // 图片资源的ID    private String pictureName ; // 图片的名称描述    public int getPictureResId() {        return pictureResId;    }    public void setPictureResId(int pictureResId) {        this.pictureResId = pictureResId;    }    public String getPictureName() {        return pictureName;    }    public void setPictureName(String pictureName) {        this.pictureName = pictureName;    }    @Override    public String toString() {        return "PictureBean{" +                "pictureResId=" + pictureResId +                ", pictureName='" + pictureName + '\'' +                '}';    }}

好像也没见得有多长。


总结一下:其实实现可滑动控件的刷新或加载也不难,定义好接口以及相应的待实现的方法,在要使用的地方实现该接口,并填充方法,在方法里面做刷新或加载的操作。另外刷新或加载完了之后,推荐使用Handler来更新界面,至于转圈圈的界面,控制好时机,该出现的时候让他出现,不该出现的时候坚决将他干掉就行了。


参考: 感谢网上的资料,下次记得把链接和名字一并奉上,这次就低调些大笑


想要源码的可以去这两个地方,不要分,拿走不谢。我也是个菜鸟,希望跟大家共同探讨人生,哈哈哈得意


Git地址:https://github.com/1989Jiangtao/GalleryRefresh

代码下载:http://download.csdn.net/detail/u010898329/9750269


0 0
原创粉丝点击