月度归档:2015年08月

RecyclerView的上拉加载更多

@2016/03/02更新:

引入下拉刷新 android-Ultra-Pull-To-Refresh

SwipeRefreshLayout组件支持了下拉刷新,在这个基础上改造RecyclerView上拉加载更多。

实现思路:

  1. listView加个底部view(footer_view)
  2. 在listview到底部的时候,自动加载。
  3. footer_view 展示提示文字和加载状态(菊花)

增加一个footer_view.xml

1.MaterialProgressBarSupport:从SwipeRefreshLayout组件中抠出来的,用作加载的转菊花。
2.一行文字提示
3.居中

<?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="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="horizontal">

    <LinearLayout android:layout_width="wrap_content" android:layout_height="40dp" android:layout_centerHorizontal="true">

        <com.github.captain_miao.recyclerviewutils.MaterialProgressBarSupport android:id="@+id/progress_view" android:layout_width="40dp" android:layout_height="40dp" app:progressColor="@color/progress_bar_color" app:progressBackgroundColor="@color/progress_bar_background_light" app:progressSmallSize="true"/>

        <TextView android:id="@+id/tv_content" android:paddingLeft="8dp" android:layout_width="wrap_content" android:layout_height="40dp" android:gravity="center_vertical" android:text="@string/app_loading_more" />
    </LinearLayout>
</RelativeLayout>

RecyclerView.Adapter增加一个底部view(footer_view)

1. 定义一个FOOTER_TYPE
2. 实现FOOTER_TYPE的onCreateViewHolder
3. 实现FOOTER_TYPE的onBindViewHolder
4. 留出item的onCreateViewHolder接口:abstract VH onCreateItemViewHolder()
5. 留出item的onBindViewHolder接口:abstract void onBindItemViewHolder()

    //footer_view 的 ViewHolder
    public static class FooterViewHolder extends RecyclerView.ViewHolder {
        public final MaterialProgressBarSupport mProgressView;
        public final TextView mTextView;

        public FooterViewHolder(View view) {
            super(view);
            mProgressView = (MaterialProgressBarSupport) view.findViewById(R.id.progress_view);
            mTextView = (TextView) view.findViewById(R.id.tv_content);
        }

        @Override
        public String toString() {
            return super.toString() + " '" + mTextView.getText();
        }
    }


    //数据itemViewHolder 实现
    public abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType);

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if(viewType == TYPE_FOOTER){//底部 加载view
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.item_view_load_more, parent, false);
            return new FooterViewHolder(view);
        } else {
            //数据itemViewHolder
            return onCreateItemViewHolder(parent, viewType);
        }
    }

    //数据itemViewHolder 实现
    public abstract void onBindItemViewHolder(final VH holder, int position);

    @Override
    @SuppressWarnings("unchecked")
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
        if(holder instanceof FooterViewHolder) {
            //没有更多数据
            if(hasMoreData){
                ((FooterViewHolder) holder).mProgressView.setVisibility(View.VISIBLE);
                ((FooterViewHolder) holder).mProgressView.startProgress();
                //((FooterViewHolder) holder).mProgressView.setIndeterminate(true);
                ((FooterViewHolder) holder).mTextView.setText(R.string.app_loading_more);
            } else {
                ((FooterViewHolder) holder).mProgressView.stopProgress();
                ((FooterViewHolder) holder).mProgressView.setVisibility(View.GONE);
                //((FooterViewHolder) holder).mProgressView.st;
                ((FooterViewHolder) holder).mTextView.setText(R.string.app_no_more_data);
            }
        } else {
            onBindItemViewHolder((VH)holder, position);
        }
    }

    @Override
    public int getItemViewType(int position) {

        if (position == getBasicItemCount() &amp;&amp; hasFooter) {
            return TYPE_FOOTER;
        }
        return super.getItemViewType(position);//0
    }

RecyclerView增加一个到达底部监听

public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
    private int previousTotal = 0; // The total number of items in the dataset after the last load
    private boolean loading = false;
    //list到达 最后一个item的时候 触发加载
    private int visibleThreshold = 1;
    // The minimum amount of items to have below your current scroll position before loading more.
    int firstVisibleItem, visibleItemCount, totalItemCount;
    //默认第一页
    private int current_page = 1;

    private LinearLayoutManager mLinearLayoutManager;

    public EndlessRecyclerOnScrollListener(LinearLayoutManager linearLayoutManager) {
        this.mLinearLayoutManager = linearLayoutManager;
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        visibleItemCount = recyclerView.getChildCount();
        totalItemCount = mLinearLayoutManager.getItemCount();
        firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();

        //判断加载完成了...
        if (loading) {
            if (totalItemCount &gt; previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading &amp;&amp; totalItemCount &gt;visibleItemCount &amp;&amp; (totalItemCount - visibleItemCount) &lt;= (firstVisibleItem + visibleThreshold)) {
            current_page++;
            onLoadMore(current_page);
            loading = true;
        }
    }

    public abstract void onLoadMore(int current_page);
}

RecyclerView中使用

        mRecyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(mLinearLayoutManager) {
            @Override
            public void onLoadMore(int current_page) {
                mAdapter.setHasFooter(true);
                //do something
            }
        });
    //RecyclerViewAdapter 实现
    public static class SimpleStringRecyclerViewAdapter
            extends BaseLoadMoreRecyclerAdapter&lt;String, ItemViewHolder&gt; {

        public SimpleStringRecyclerViewAdapter(Context context, List&lt;String&gt; items) {
            appendToList(items);
        }


        @Override
        public ItemViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.list_item, parent, false);
            return new ItemViewHolder(view);
        }

        @Override
        public void onBindItemViewHolder(ItemViewHolder holder, int position) {
            holder.mTextView.setText(getItem(position));
            holder.mImageView.setImageResource(R.mipmap.ic_launcher);
        }
    }

load_more

搞定收工

GitHub地址:

GitHub地址:RecyclerViewUtils

RecyclerView和CardView使用

预备知识

RecyclerView是Android5.0中support library的新控件,可以说是ListView的重新设计,但使用上更“麻烦”一点。
ListView使用中为了效率,使用了ViewHolder,RecyclerView将ViewHolder集成进来,必须实现。
使用RecyclerView, 需要实现一个Adapter和一个layout manager(用来维护item view)。 Adapter继承RecyclerView.Adapter,来完成itemView的绑定。
如下图:
RecyclerView

RecyclerView provides these built-in layout managers:
LinearLayoutManager shows items in a vertical or horizontal scrolling list.
GridLayoutManager shows items in a grid.
StaggeredGridLayoutManager shows items in a staggered grid.

添加依赖

dependencies {
        compile 'com.android.support:cardview-v7:22.2.1'
        compile 'com.android.support:recyclerview-v7:22.2.1'
 }

ViewHolder

ViewHolder就是itemView的布局缓存机制,只需要一次findViewById()。

        public static class ItemViewHolder extends RecyclerView.ViewHolder {
            public final ImageView mImageView;
            public final TextView mTextView;

            public ItemViewHolder(View view) {
                super(view);
                mImageView = (ImageView) view.findViewById(R.id.avatar);
                mTextView = (TextView) view.findViewById(R.id.tv_content);
            }

            @Override
            public String toString() {
                return super.toString() + " '" + mTextView.getText();
            }
        }

RecyclerViewAdapter

    //创建ViewHolder,inflate(R.layout.list_item)生产view,再new ViewHolder()完成
    onCreateViewHolder()
    //有了viewHolder和数据之后,进行绑定咯。
    onBindViewHolder()
    public static class SimpleStringRecyclerViewAdapter
            extends RecyclerView.Adapter<ItemViewHolder> {
        private List<String> mValues;


        public String getValueAt(int position) {
            return mValues.get(position);
        }

        public SimpleStringRecyclerViewAdapter(Context context, List<String> items) {
            mValues = items;
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.list_item, parent, false);
            return new ItemViewHolder(view);
        }

        @Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
            ((ItemViewHolder)holder).mTextView.setText(mValues.get(position));
            ((ItemViewHolder)holder).mImageView.setImageResource(R.mipmap.ic_launcher);         
        }

        @Override
        public int getItemCount() {
            return mValues.size();
        }
    }

布局文件

RecyclerView的布局

<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main_swipe_refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"/>

</android.support.v4.widget.SwipeRefreshLayout>

CardView就是一个类似卡片的样子

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/tools"
    android:id="@+id/card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    card_view:cardCornerRadius="4dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="8dp"
        android:paddingLeft="8dp"
        android:paddingRight="8dp"
        android:paddingTop="8dp">

        <ImageView
            android:id="@+id/avatar"
            android:layout_width="@dimen/list_item_avatar_size"
            android:layout_height="@dimen/list_item_avatar_size"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:layout_marginRight="16dp" />

        <TextView
            android:id="@+id/tv_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/avatar" />
    </RelativeLayout>
</android.support.v7.widget.CardView>

展示list

        mRecyclerView.setLayoutManager(mLinearLayoutManager = new LinearLayoutManager(getActivity()));
        mAdapter = new SimpleStringRecyclerViewAdapter(getActivity(), mDataList);
        mRecyclerView.setAdapter(mAdapter);

rv

Animations

用默认的,还不错。

    view.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int itemPosition = SwipeRefreshFragmentList.mRecyclerView.getChildAdapterPosition(v);
            mValues.remove(itemPosition);
            notifyItemRemoved(itemPosition);
        }
    });

对item定义动画,以后再表啊。

搞定收工

GitHub地址:https://github.com/captain-miao/AndroidStartupDemo
apk下载地址:http://fir.im/yan

Android5.0版本的下拉刷新SwipeRefreshLayout使用

交流可入QQ群:436275452(Android Android Android)

Android5.0的Material Design,是时候跟进,使用了。Android5.0设备占比已经12%了,可以这里查看:android dashboards

环境配置

Android Studio 刚刚更新至1.3,环境配置如下:@2015.08.01

classpath 'com.android.tools.build:gradle:1.2.3'
compile 'com.android.support:appcompat-v7:22.2.1'

效果图来一张

SwipeRefreshLayout

写个布局文件

1. SwipeRefreshLayout需要是父级
2. 只能有一个子view

<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main_swipe_refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="@string/app_name" />

</android.support.v4.widget.SwipeRefreshLayout>

SwipeRefreshLayout使用(非常简单)

1. setOnRefreshListener()触发刷新回调方法
2. setRefreshing(false)设置刷新完成,进度圆圈圈停止转动。
3. setEnabled(false)可以禁止下拉刷新

    mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.activity_main_swipe_refresh_layout);
    //设置加载圈的背景颜色
    mSwipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.line_color_run_speed_1);
    //设置加载圈圈的颜色
    mSwipeRefreshLayout.setColorSchemeResources(R.color.line_color_run_speed_7,
            R.color.line_color_run_speed_9,
            R.color.line_color_run_speed_11);
    mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            //
            mSwipeRefreshLayout.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mSwipeRefreshLayout.setRefreshing(false);
                }
            }, 3000);//3秒
        }
    });

搞定收工

GitHub地址:https://github.com/captain-miao/AndroidStartupDemo