原文來自個人微信公衆號: longkai_1991java
先上圖,看效果:node
前幾天剛release完公司的一個項目,有了點時間,因而就想找一些有意思的東西學習一下,順便運用在項目之中。看到iOS的同事們在談論iOS8的xx特性時,我忽然也有想在公司項目的下一個版本中添加Android L版本的特性。android
六月底的時候收看Google io時,當時對Android新的設計語言,Material Design,沒什麼太大的好感,感受色彩一坨一坨的,好難看的樣子,當時以爲亮點就是新的ART運行時環境和一些酷炫的動畫效果。再後來,8月初的時候,本身出於好奇真的拿Nexus 5安裝了一個L的預覽版,體驗不好...好多軟件都仍是holo的,反正以爲不是很期待就是啦。json
回到重點,下載好最新的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推出正式版的。app
廢話扯了那麼多,下面纔是今天的主題,super fast listview,歷來沒有見過這樣快的list,甚至還支持橫向的滾動,要知道,這在以前的Android,要實現橫向的list是有多蛋疼!還有更多的驚喜,在另外一個兼容庫leanback-v17
中,還有Grid,StagedGrid,HorizonalGrid等更高級的Widget,知道Pinterest的瀑布流麼?ide
下面的代碼,提供了滑動到底部自動加載更多的功能,是我本身根據之前listview的經驗寫的,因爲加載的速度過快,在刪除加載更多的提示時,有時會出現頁面有一部分空白間距的問題,沒辦法,只好postdelay 50毫秒,再將加載回來的list追加到末尾。佈局
下面是源代碼,使用recycle view配合card view實現無限list(自動帶提示加載更多,而且包含不用類型的view),super fast~ 看這段代碼前但願你能先去Material Design的主頁看看基本介紹和範例代碼。post
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" />
最後附一張效果圖,比較醜,只是爲了演示而已
因爲是公司的項目,因此比較詳細的代碼沒有貼出來,可是也是依據這段代碼弄出來的,有時間的話,改天封裝一個出來~
by longkai on 1 Sep. in Sz.