首页 > 代码库 > Android RecyclerView: Super Fast ListView 超级快速的Lis

Android RecyclerView: Super Fast ListView 超级快速的Lis

Android RecyclerView: Super Fast ListView

原文来自我的微信公众号: longkai_1991

先上图,看效果:

img

前几天刚release完公司的一个项目,有了点时间,于是就想找一些有意思的东西学习一下,顺便运用在项目之中。看到iOS的同事们在谈论iOS8的xx特性时,我突然也有想在公司项目的下一个版本中添加Android L版本的特性。

六月底的时候收看Google io时,当时对Android新的设计语言,Material Design,没什么太大的好感,感觉色彩一坨一坨的,好难看的样子,当时觉得亮点就是新的ART运行时环境和一些酷炫的动画效果。再后来,8月初的时候,自己出于好奇真的拿Nexus 5安装了一个L的预览版,体验很差…好多软件都还是holo的,反正觉得不是很期待就是啦。

回到重点,下载好最新的SDK,你会发现在ANDROID_HOME/extras/android/m2repository/com/android/support下面多了不少兼容库,cardview, support-annotations, recyclerview-v7,眼前一亮吧~这回,Google真的是拿出了好多东西呀,赞,尤其是cardview和recyclerview这两个新的控件,这个在Google最新的Material Design主页上有说明和简单的介绍,简而言之,cardview可以提供和Google很多自家应用观感一致的卡片化布局,而recyclerview则是一个增强版的listview,更强大和好用。

手痒了,特别想试试,但是这里有一个坑,因为仍旧是预览版,所以Google把minSDKVersion设置成了L,意思就是只有使用L预览版系统的机器才可以测试。呵呵,广大人民群众怎么会被这个给吓到,网上有在AndroidManifest.xml中设置<uses-sdk tools:node="replace" />即可。还有另一招,将源码解压出来,然后自己按照项目结构放置源码文件,最后再在自己的项目中引入就好了,但是要注意一点,需要把L版本相关的代码给删掉,无所谓啦,反正到时候Google推出正式版的。

废话扯了那么多,下面才是今天的主题,super fast listview,从来没有见过这样快的list,甚至还支持横向的滚动,要知道,这在之前的Android,要实现横向的list是有多蛋疼!还有更多的惊喜,在另一个兼容库leanback-v17中,还有Grid,StagedGrid,HorizonalGrid等更高级的Widget,知道Pinterest的瀑布流麽?

下面的代码,提供了滑动到底部自动加载更多的功能,是我自己根据以前listview的经验写的,由于加载的速度过快,在删除加载更多的提示时,有时会出现页面有一部分空白间距的问题,没办法,只好postdelay 50毫秒,再将加载回来的list追加到末尾。

下面是源代码,使用recycle view配合card view实现无限list(自动带提示加载更多,并且包含不用类型的view),super fast~ 看这段代码前希望你能先去Material Design的主页看看基本介绍和范例代码。

MainActivity.java

/*
 * Copyright (c) 2014 longkai
 * The software shall be used for good, not evil.
 */

package com.example.gridlayout;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.manuelpeinado.refreshactionitem.RefreshActionItem;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class MainActivity extends Activity {
  public static final String TAG = MainActivity.class.getSimpleName();

  public static final String TYPE = "type";
  public static final int ITEM = 0;
  public static final int SIMPLE = 1;
  public static final int FOOTER = 2;

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState == null) {
      try {
        getFragmentManager().beginTransaction()
            .replace(android.R.id.content, CardFragment.class.newInstance())
            .commit();
      } catch (InstantiationException e) {
        e.printStackTrace();
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      }
    }
  }

  @Override public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }

  @Override public boolean onMenuItemSelected(int featureId, MenuItem item) {
    switch (item.getItemId()) {
      case R.id.action_settings:
        break;
      default:
        break;
    }
    return super.onMenuItemSelected(featureId, item);
  }

  public static class CardFragment extends Fragment {

    boolean loading = false;

    Handler mHandler = new Handler();
    RecyclerView mRecyclerView;
    CardAdapter mAdapter;

    @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      View view = inflater.inflate(R.layout.recycler, container, false);
      mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler);
      return view;
    }

    @Override public void onViewCreated(View view, Bundle savedInstanceState) {
      super.onViewCreated(view, savedInstanceState);
    }

    @Override public void onActivityCreated(Bundle savedInstanceState) {
      super.onActivityCreated(savedInstanceState);
      List<JSONObject> list = new ArrayList<>();
      try {
        for (int i = 0; i < 300; i++) {
          JSONObject jsonObject = new JSONObject();
          if (i % 10 == 0) {
            jsonObject.put(TYPE, SIMPLE);
          } else {
            jsonObject.put(TYPE, ITEM);
          }
          list.add(jsonObject);
        }
      } catch (JSONException ignore) {
      }
      mAdapter = new CardAdapter(list);
      mRecyclerView.setHasFixedSize(true);
      mRecyclerView.setAdapter(mAdapter);
      LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
      layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
      mRecyclerView.setLayoutManager(layoutManager);
      mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override public void onScrollStateChanged(int newState) {

        }

        @Override public void onScrolled(int dx, int dy) {
          if (!loading && layoutManager.findLastVisibleItemPosition() == list.size() - 1) {
            loading = true;
            JSONObject jsonObject = new JSONObject();
            try {
              jsonObject.put(TYPE, FOOTER);
            } catch (JSONException ignore) {
            }
            mAdapter.add(jsonObject);
            new Thread(() -> {
              try {
                TimeUnit.SECONDS.sleep(3);
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
              List<JSONObject> tmp = new ArrayList<JSONObject>();
              for (int i = 0; i < 10; i++) {
                JSONObject json = new JSONObject();
                try {
                  json.put(TYPE, ITEM);
                } catch (JSONException e) {
                }
                tmp.add(json);
              }
              mHandler.post(() -> {
                mAdapter.remove(mAdapter.getItemCount() - 1);
                mAdapter.addAll(tmp);
                loading = false;
              });
            }).start();
          }
        }
      });
    }
  }

  private static class CardAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<JSONObject> list;

    private CardAdapter(List<JSONObject> list) {
      this.list = list;
    }

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

    @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
      switch (viewType) {
        case SIMPLE:
          return new RecyclerView.ViewHolder(LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false)) {
          };
        case FOOTER:
          return new RecyclerView.ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.load_more, parent, false)) {
          };
        default:
        case ITEM:
          View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card, parent, false);
          CardViewHolder holder = new CardViewHolder(view);
          return holder;
      }
    }

    @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
      switch (list.get(position).optInt(TYPE)) {
        case ITEM:
          CardViewHolder cardViewHolder = (CardViewHolder) holder;
          cardViewHolder.textView.setText("item " + position);
          break;
        case SIMPLE:
          TextView txt = (TextView) holder.itemView.findViewById(android.R.id.text1);
          txt.setText("simple txt!");
          Log.d(TAG, "simple text");
          break;
        case FOOTER:
          Log.d(TAG, "footer!");
          break;
      }
    }

    @Override public int getItemViewType(int position) {
      return list.get(position).optInt(TYPE);
    }

    public void add(JSONObject jsonObject) {
      this.list.add(jsonObject);
      notifyItemInserted(list.size() - 1);
    }

    public void addAll(List<JSONObject> list) {
      this.list.addAll(list);
      notifyDataSetChanged();
    }

    public void remove(int i) {
      list.remove(i);
      notifyItemRemoved(i);
    }

    static class CardViewHolder extends RecyclerView.ViewHolder {
      TextView textView;

      CardViewHolder(View view) {
        super(view);
        textView = (TextView) view.findViewById(R.id.txt);
      }
    }
  }
}

以下是布局文件,非常简单

card.xml

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    style="@style/CardView.Light"
    card_view:cardCornerRadius="4dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

  <TextView
      android:id="@+id/txt"
      android:textAppearance="?android:textAppearanceMedium"
      android:gravity="center"
      android:layout_width="match_parent"
      android:layout_height="200dp" />
</android.support.v7.widget.CardView>

recycler.xml

<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/recycler"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

最后附一张效果图,比较丑,只是为了演示而已

simple

由于是公司的项目,所以比较详细的代码没有贴出来,但是也是依据这段代码弄出来的,有时间的话,改天封装一个出来~


by longkai on 1 Sep. in Sz.

Android RecyclerView: Super Fast ListView 超级快速的Lis