編輯:關於Android編程
RecyclerView是一種列表容器, 發布很久了, 才想起來寫點什麼.
RecyclerView相比於ListView, 在回收重用時更具有靈活性, 也就是低耦合, 並且提供了擴展. 加載多個視圖時, 應該多用RecyclerView代替ListView.
那麼我們來看看這東西應該怎麼用? 比如生成一個瀑布流的視圖.
首先我們從一個HelloWorld寫起, 看看如何構建一個RecyclerView.
1. 依賴庫
Gradle配置, 添加Recycler庫
compile 'com.android.support:recyclerview-v7:+'
2. 資源文件
資源文件
<android.support.v7.widget.RecyclerView android:id="@+id/test_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"/>3. 代碼
LayoutManager\Adapter是必須, ItemDecoration\ItemAnimator是可選. /** * 初始化RecyclerView * * @param recyclerView 主控件 */ private void initRecyclerView(RecyclerView recyclerView) { recyclerView.setHasFixedSize(true); // 設置固定大小 initRecyclerLayoutManager(recyclerView); // 初始化布局 initRecyclerAdapter(recyclerView); // 初始化適配器 initItemDecoration(recyclerView); // 初始化裝飾 initItemAnimator(recyclerView); // 初始化動畫效果 }
4. LayoutManager
管理RecyclerView的布局結構.
private void initRecyclerLayoutManager(RecyclerView recyclerView) { // 錯列網格布局 recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL)); }
提供了多種LayoutManager, 瀑布流使用錯列網格布局.
5. Adapter
適配器, 處理RecyclerView的Item事務.
private void initRecyclerAdapter(RecyclerView recyclerView) { mAdapter = new MyAdapter(getData()); recyclerView.setAdapter(mAdapter); }
對於Adapter, 我們需要展開來說, 先看看類.
public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> { private List<DataModel> mDataModels; private List<Integer> mHeights; MyAdapter(List<DataModel> dataModels) { if (dataModels == null) { throw new IllegalArgumentException("DataModel must not be null"); } mDataModels = dataModels; mHeights = new ArrayList<>(); } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_recycler_view, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { DataModel dataModel = mDataModels.get(position); // 隨機高度, 模擬瀑布效果. if (mHeights.size() <= position) { mHeights.add((int) (100 + Math.random() * 300)); } ViewGroup.LayoutParams lp = holder.getTvLabel().getLayoutParams(); lp.height = mHeights.get(position); holder.getTvLabel().setLayoutParams(lp); holder.getTvLabel().setText(dataModel.getLabel()); holder.getTvDateTime().setText(new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) .format(dataModel.getDateTime())); } @Override public int getItemCount() { return mDataModels.size(); } public void addData(int position) { DataModel model = new DataModel(); model.setDateTime(getBeforeDay(new Date(), position)); model.setLabel("No. " + (int) (new Random().nextDouble() * 20.0f)); mDataModels.add(position, model); notifyItemInserted(position); } public void removeData(int position) { mDataModels.remove(position); notifyItemRemoved(position); } /** * 獲取日期的前一天 * * @param date 日期 * @param i 偏離 * @return 新的日期 */ private static Date getBeforeDay(Date date, int i) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); calendar.add(Calendar.DAY_OF_YEAR, i * (-1)); return calendar.getTime(); } }
(1)onCreateViewHolder創建ViewHolder.
(2)onBindViewHolder綁定每一項數據.
(3)getItemCount返回列表長度.
(4)RecyclerView強制使用ViewHolder.
public class MyViewHolder extends RecyclerView.ViewHolder { private TextView mTvLabel; // 標簽 private TextView mTvDateTime; // 日期 public MyViewHolder(View itemView) { super(itemView); mTvLabel = (TextView) itemView.findViewById(R.id.item_text); mTvDateTime = (TextView) itemView.findViewById(R.id.item_date); } public TextView getTvLabel() { return mTvLabel; } public TextView getTvDateTime() { return mTvDateTime; } }
在onCreateViewHolder方法, 創建類; 在onBindViewHolder方法, 綁定數據.
DataModel
public class DataModel { private String mLabel; private Date mDateTime; public String getLabel() { return mLabel; } public void setLabel(String label) { mLabel = label; } public Date getDateTime() { return mDateTime; } public void setDateTime(Date dateTime) { mDateTime = dateTime; } }
6. ItemDecoration
項的裝飾, 比如ListView中的分割線, 在本例中, 左右兩條粉線.
private void initItemDecoration(RecyclerView recyclerView) { recyclerView.addItemDecoration(new MyItemDecoration(this)); }
ItemDecoration, 注意parent和child的使用方式.
public class MyItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; private Drawable mDivider; public MyItemDecoration(Context context) { final TypedArray array = context.obtainStyledAttributes(ATTRS); mDivider = array.getDrawable(0); array.recycle(); } @Override public void onDraw(Canvas c, RecyclerView parent, State state) { drawHorizontal(c, parent); drawVertical(c, parent); } // 水平線 public void drawHorizontal(Canvas c, RecyclerView parent) { final int childCount = parent.getChildCount(); // 在每一個子控件的底部畫線 for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final int left = child.getLeft() + child.getPaddingLeft(); final int right = child.getWidth() + child.getLeft() - child.getPaddingRight(); final int top = child.getBottom() - mDivider.getIntrinsicHeight() - child.getPaddingBottom(); final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } // 豎直線 public void drawVertical(Canvas c, RecyclerView parent) { final int childCount = parent.getChildCount(); // 在每一個子控件的右側畫線 for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); int right = child.getRight() - child.getPaddingRight(); int left = right - mDivider.getIntrinsicWidth(); final int top = child.getTop() + child.getPaddingTop(); final int bottom = child.getTop() + child.getHeight() - child.getPaddingBottom(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } // Item之間的留白 @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } }
本例重寫了listDivider
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> ... <item name="android:listDivider">@drawable/divider_bg</item> </style> <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#ff00ff"/> <size android:height="4dp"/> <size android:width="4dp"/> </shape>
7. ItemAnimator
動畫效果比較復雜, 使用默認動畫. 如要定制的話, 繼承DefaultItemAnimator; 如設置null, 則不顯示任何動畫.
private void initItemAnimator(RecyclerView recyclerView) { recyclerView.setItemAnimator(new DefaultItemAnimator()); // 默認動畫 }
8. 最終Activity
public class MainActivity extends AppCompatActivity { private MyAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); // 初始化RecyclerView initRecyclerView((RecyclerView) findViewById(R.id.test_recycler_view)); } /** * 初始化RecyclerView * * @param recyclerView 主控件 */ private void initRecyclerView(RecyclerView recyclerView) { recyclerView.setHasFixedSize(true); // 設置固定大小 initRecyclerLayoutManager(recyclerView); // 初始化LayoutManager initRecyclerAdapter(recyclerView); // 初始化Adapter initItemDecoration(recyclerView); // 初始化邊界裝飾 initItemAnimator(recyclerView); // 初始化動畫效果 } /** * 初始化RecyclerView的LayoutManager * * @param recyclerView 主控件 */ private void initRecyclerLayoutManager(RecyclerView recyclerView) { // 錯列網格布局 recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL)); } /** * 初始化RecyclerView的Adapter * * @param recyclerView 主控件 */ private void initRecyclerAdapter(RecyclerView recyclerView) { mAdapter = new MyAdapter(getData()); recyclerView.setAdapter(mAdapter); } /** * 初始化RecyclerView的(ItemDecoration)項目裝飾 * * @param recyclerView 主控件 */ private void initItemDecoration(RecyclerView recyclerView) { recyclerView.addItemDecoration(new MyItemDecoration(this)); } /** * 初始化RecyclerView的(ItemAnimator)項目動畫 * * @param recyclerView 主控件 */ private void initItemAnimator(RecyclerView recyclerView) { recyclerView.setItemAnimator(new DefaultItemAnimator()); // 默認動畫 } /** * 模擬的數據 * * @return 數據 */ private ArrayList<DataModel> getData() { int count = 57; ArrayList<DataModel> data = new ArrayList<>(); for (int i = 0; i < count; i++) { DataModel model = new DataModel(); model.setDateTime(getBeforeDay(new Date(), i)); model.setLabel("No. " + i); data.add(model); } return data; } /** * 獲取日期的前一天 * * @param date 日期 * @param i 偏離 * @return 新的日期 */ private static Date getBeforeDay(Date date, int i) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); calendar.add(Calendar.DAY_OF_YEAR, i * (-1)); return calendar.getTime(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); switch (item.getItemId()) { case R.id.id_action_add: mAdapter.addData(1); break; case R.id.id_action_delete: mAdapter.removeData(1); break; } //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
為了測試動畫, Menu額外添加兩個按鈕。
9.運行ReactNative示例
既然感覺ReactNative開發靠譜, 那麼我們就來看看ReactNative都能做哪些好玩的東西, 和原生的有哪些區別?
示例圖
按照文檔安裝一些命令行工具, 再下載Git代碼.
Github: https://github.com/facebook/react-native
內容很多, 包含一些依賴庫和示例(Example), 下載的有點慢, 耐心等待.
下載完成後, 在react-native內, 執行npm install.
Android項目執行, 參考ReactAndroid的README.md.
在react-native目錄, 新建local.properties
sdk.dir=/Users/wangchenlong/Installations/android-sdk ndk.dir=/Users/wangchenlong/Installations/android-ndk-r10e
執行
cd react-native ./gradlew :ReactAndroid:assembleDebug
再執行
./gradlew :ReactAndroid:installArchives
啟動服務
./packager/packager.sh
安裝項目
cd react-native ./gradlew :Examples:UIExplorer:android:app:installDebug
一定要先啟動服務, 再安裝項目.
出現transforming 100%, 即導入成功.
在最新版本中, 我的紅米note4無法運行項目.
報錯: Upload package to device fails.
原因是編譯的gradle版本太高, 默認1.5.0, 實際1.2.0~1.3.0都可以運行.
我的是1.2.3.
真機調試, 本人紅米note(Android 4.2)
搖動手機, 選擇Dev Settings->Debug sever host & port for device. 設置IP地址, 觀察本機的IP, 填入即可. 我當前的是
192.168.2.202:8081
注意設置端口8081, 否則無法加載. 有些情況可以直接輸入IP即可.
Android5.0以上, 直接設置端口即可.
adb reverse tcp:8081 tcp:8081
參考Android的真機調試文檔.
IOS模擬器, 太窮沒有iPhone. 直接打開open UIExplorer.xcodeproj項目, 執行就可以顯示.
開發有兩種選擇, 一種是直接基於ReactNative開發, 一種是把ReactNative集成到現有的App中, 對於第二種, 我們就需要關注, ReactNative會增大多少代碼呢?
使用最基本的HelloWorld做測試, ReactNative也是生成一個簡單HelloWorld的JS. 最新生成的HelloWorld的大小是1.4M, 加上ReactNative的是7.6M, 框架大約6.2M左右, 各位可以權衡一下使用.
ReactNative的UIExplorer已經包含了大量示例, 很接近原生, 非常絢麗, 速度也很快. 如Android的ViewPager
OK, 好的開始是成功的一半, 繼續探索吧! Enjoy it!
1. Overview這是一篇翻譯文章,自己作為android工程師以來,第一次翻譯國外網站的文章,所以不免會有一些遺漏和錯誤。原文請參考RecyclerView 是一種
先來看下效果圖: 其中show和dismiss的時候有動畫效果。 原先試過使用PopupWindow來做,但是使用的時候不是那麼舒服,畢竟不
首先apk不能被代碼混淆(或未經編譯優化),如果混淆了,反編譯出來的代號還是看不懂, 當然,在你沒反編譯出來之前,你也不知道有沒有混淆。 網上各種反編譯工具,&
本文實例講述了Android實現捕獲TextView超鏈接的方法。分享給大家供大家參考,具體如下:這裡分享一篇捕獲TextView超鏈接的文章,希望對大家有所幫助,我終於