版權聲明:本文爲HaiyuKing原創文章,轉載請註明出處!html
VirtualLayout是一個針對RecyclerView的LayoutManager擴展, 主要提供一整套佈局方案和佈局間的組件複用的問題。java
經過定製化的LayoutManager,接管整個RecyclerView的佈局邏輯;LayoutManager管理了一系列LayoutHelper,LayoutHelper負責具體佈局邏輯實現的地方;每個LayoutHelper負責頁面某一個範圍內的組件佈局;不一樣的LayoutHelper能夠作不一樣的佈局邏輯,所以能夠在一個RecyclerView頁面裏提供異構的佈局結構,這就能比系統自帶的LinearLayoutManager、GridLayoutManager等提供更加豐富的能力。同時支持擴展LayoutHelper來提供更多的佈局能力。android
這個demo只是簡單記錄下上面黃色標記的幾種佈局的實現。git
vlayout1.2.8版本中使用的recyclerview是23.1.1版本,v4是23.1.1,這個須要注意。若是項目中用到recyclerview和v4包的話,建議依賴新版本的recyclerview和v4。github
下面是vlayout的build.gradle文件的部分源碼:json
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
// compile project(':extension')
if (project.hasProperty('useNewSupportLibrary')) {
compile 'com.android.support:recyclerview-v7:25.2.0@aar'
compile('com.android.support:support-v4:25.2.0@aar')
compile 'com.android.support:support-annotations:25.2.0'
compile 'com.android.support:support-compat:25.2.0'
compile 'com.android.support:support-core-ui:25.2.0'
} else {
compile 'com.android.support:recyclerview-v7:23.1.1@aar'
compile('com.android.support:support-v4:23.1.1@aar') {
exclude group: 'com.android.support', module: 'support-annotations'
}
compile 'com.android.support:support-annotations:23.1.1'
}
androidTestCompile "org.robolectric:robolectric:3.0"
}
因此在項目中app目錄的build.gradle中依賴vlayout時,會有這樣的提醒:數組
注意事項:app
一、 導入類文件後須要change包名以及從新import R文件路徑框架
二、 Values目錄下的文件(strings.xml、dimens.xml、colors.xml等),若是項目中存在,則複製裏面的內容,不要整個覆蓋ide
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.why.project.vlayoutdemo"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//Vlayout implementation ('com.alibaba.android:vlayout:1.2.8@aar') { transitive = true }
//下面的代碼,根據實際狀況添加,若是項目中用到了下面的庫,則須要根據項目依賴高版本庫,不然就會使用vlayout中的低版本庫 //RecyclerView【由於vlayout中的recyclerview版本低,因此須要從新依賴高版本,不然app中使用recyclerview的話會直接使用vlayout中的低版本recyclerview】 implementation "com.android.support:recyclerview-v7:28.0.0" //v4 implementation 'com.android.support:support-v4:28.0.0'
}
這樣就將vlayout集成到項目中了。
{ "data": [ { "modelname": "推送新聞", "data": [ { "image": "https://imgsa.baidu.com/news/q%3D100/sign=06d12a2732dbb6fd235be1263925aba6/b151f8198618367a8dbfe10125738bd4b31ce53c.jpg", "url": "http://news.ifeng.com/a/20180120/55302258_0.shtml?_zbs_baidu_news", "title": "安徽合肥:「非遺」進校園", "id": "whyidzhi", "order": "1" } ] }, { "modelname": "焦點新聞", "data": [ { "image": "https://b.bdstatic.com/boxlib/20180120/2018012017100383423448679.jpg", "url": "http://pic.chinadaily.com.cn/2018-01/20/content_35544757.htm", "title": "西昌鐵路警方用表情包宣傳愛路小知識", "id": "whyidzhi", "order": "1" }, { "image": "https://b.bdstatic.com/boxlib/20180120/2018012017100311270281486.jpg", "url": "http://pic.chinadaily.com.cn/2018-01/20/content_35544758.htm", "title": "成都熊貓基地太陽產房全新升級", "id": "whyidzhi", "order": "2" }, { "image": "https://b.bdstatic.com/boxlib/20180120/2018012017100392134086973.jpg", "url": "http://pic.chinadaily.com.cn/2018-01/20/content_35544759.htm", "title": "長沙「90後」交警用手繪記錄交警故事", "id": "whyidzhi", "order": "3" } ] }, { "modelname": "國際人物", "data": [ { "image": "http://t12.baidu.com/it/u=264843290,870648681&fm=173&s=29C043870E531CDC082897A003006015&w=218&h=146&img.JPEG", "url": "http://news.163.com/18/0120/15/D8JS570V00018AOQ_all_mobile.html", "title": "法總統馬克龍會見默克爾 強調歐洲改革須要德國(全文..", "id": "whyidzhi", "order": "1" }, { "image": "", "url": "http://www.yidianzixun.com/article/0IBPUBcu", "title": "默克爾組閣可否成功?全等這場黨代會", "id": "whyidzhi", "order": "2" }, { "image": "", "url": "http://www.yidianzixun.com/article/0IBMT9yr", "title": "馬克龍向默克爾表支持 強調推進歐洲改革須要德國", "id": "whyidzhi", "order": "3" }, { "image": "", "url": "http://news.sina.com.cn/w/2018-01-20/doc-ifyquixe4989529.shtml", "title": "德法首腦在巴黎會晤 默克爾:歐洲須要穩定的德國政府", "id": "whyidzhi", "order": "4" }, { "image": "", "url": "http://news.sina.com.cn/o/2018-01-20/doc-ifyqtycx0664636.shtml", "title": "德國社民黨民衆支持率創新低", "id": "whyidzhi", "order": "5" } ] }, { "modelname": "圖片新聞", "data": [ { "image": "https://t10.baidu.com/it/u=44207055,3448633405&fm=173&s=CFF20CC150452CEC9F9C491103005092&w=218&h=146&img.JPEG", "url": "http://war.163.com/18/0120/11/D8JEFM5D000181KT_mobile.html", "title": "外媒稱朝鮮先遣隊取消訪韓", "id": "whyidzhi", "order": "1" }, { "image": "https://t10.baidu.com/it/u=1625316837,2876937793&fm=173&s=F507703317115D6644753AE80300E036&w=218&h=146&img.JPEG", "url": "http://news.china.com/internationalgd/10000166/20180120/31985304.html", "title": "狂風橫掃歐洲多國,致10人", "id": "whyidzhi", "order": "2" }, { "image": "https://t10.baidu.com/it/u=2946973968,4057971581&fm=173&s=2D216D93C2F00B9219A825E703009060&w=218&h=146&img.JPEG", "url": "http://news.sina.com.cn/w/2018-01-20/doc-ifyqtycx0654102.shtml", "title": "津巴布韋反對黨要人在美墜", "id": "whyidzhi", "order": "3" }, { "image": "https://t12.baidu.com/it/u=3788697896,2880168169&fm=173&s=D31A38C4D664D55FC29281010300309B&w=218&h=146&img.JPEG", "url": "http://finance.ifeng.com/a/20180120/15937975_0.shtml", "title": "美國政府關門 比特幣聞訊", "id": "whyidzhi", "order": "4" }, { "image": "https://t10.baidu.com/it/u=1499851317,895362806&fm=173&s=BD04DF104871339C53A88C870100E0E3&w=218&h=146&img.JPEG", "url": "http://news.sina.com.cn/o/2018-01-20/doc-ifyquixe5136803.shtml", "title": "剛剛,美國政府正式宣告", "id": "whyidzhi", "order": "5" }, { "image": "https://t12.baidu.com/it/u=4229963978,641890755&fm=173&s=74B218D646A08B491AAF3E9903001088&w=218&h=146&img.JPEG", "url": "http://www.chinanews.com/gj/2018/01-20/8428898.shtml", "title": "超三成受訪者稱中國是「新", "id": "whyidzhi", "order": "6" } ] }, { "modelname": "視頻新聞", "data": [ { "image": "http://pic.rmb.bdstatic.com/1f01839d477aa7a3ffa36f07542f8d5b.jpg@h_660,w_370", "url": "http://v.qq.com/x/page/h0527p90e2b.html", "title": "杭州餐館爆炸,路過公交車20餘人受傷", "id": "whyidzhi", "order": "1" }, { "image": "http://pic.rmb.bdstatic.com/1f541422405ab18797795c0adc3318eb.jpg@h_660,w_370", "url": "http://www.iqiyi.com/v_19rr7omigs.html", "title": "中央措辭嚴厲批祁連山生態問題 8責任人被問責", "id": "whyidzhi", "order": "2" }, { "image": "http://pic.rmb.bdstatic.com/d46f662c1900de051da6de2eef2c485f.jpg@h_660,w_370", "url": "http://v.qq.com/x/page/a0024q7bkg0.html", "title": "吉林永吉 吉林市多地遭暴雨襲擊 縣城災後清理正在進行", "id": "whyidzhi", "order": "3" }, { "image": "http://pic.rmb.bdstatic.com/b9cdfca4adeb36ac1a0730f70b9d63f9.jpg@h_660,w_370", "url": "http://www.iqiyi.com/v_19rr7oosfs.html", "title": "40 高溫下一出租車行駛中自燃", "id": "whyidzhi", "order": "4" }, { "image": "http://pic.rmb.bdstatic.com/660e5152b144e48bbdffdad5d9fdc088.jpg@h_660,w_370", "url": "http://v.qq.com/x/page/v0527bhjwh3.html", "title": "徐光裕:印度航母只是二流航母", "id": "whyidzhi", "order": "5" } ] } ] }
參考《BannerDemo【圖片輪播圖控件】》、《GlideNewDemo【Glide4.7.1版本的簡單使用以及圓角功能】》
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/vlayout_rv" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="none" android:clipToPadding="true" android:paddingLeft="0dp" android:paddingRight="0dp" android:requiresFadingEdge="none"/> </LinearLayout>
package com.why.project.vlayoutdemo; import android.content.Context; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.widget.Toast; import com.alibaba.android.vlayout.DelegateAdapter; import com.alibaba.android.vlayout.VirtualLayoutManager; import com.alibaba.android.vlayout.layout.GridLayoutHelper; import com.alibaba.android.vlayout.layout.LinearLayoutHelper; import com.alibaba.android.vlayout.layout.SingleLayoutHelper; import com.alibaba.android.vlayout.layout.StickyLayoutHelper; import com.why.project.vlayoutdemo.adapter.BannerLayoutAdapter; import com.why.project.vlayoutdemo.adapter.GridLayoutAdapter; import com.why.project.vlayoutdemo.adapter.HorizontalListLayoutAdapter; import com.why.project.vlayoutdemo.adapter.ListLayoutAdapter; import com.why.project.vlayoutdemo.adapter.StickyTitleAdapter; import com.why.project.vlayoutdemo.bean.ItemBean; import com.why.project.vlayoutdemo.bean.ModelBean; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private List<ModelBean> mModelBeanList;//列表數據集合 private RecyclerView mVLayoutRV; private VirtualLayoutManager layoutManager; private DelegateAdapter delegateAdapter; private List<DelegateAdapter.Adapter> adapters; private RecyclerView.RecycledViewPool viewPool;//設置複用池的大小 private int itemType;//一個Adapter對應一個類型,這裏經過自增長1實現惟一性 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); initDatas(); } private void initViews() { mVLayoutRV = findViewById(R.id.vlayout_rv); } private void initDatas() { //初始化列表數據集合 mModelBeanList = new ArrayList<ModelBean>(); getTestDatas(); Log.e(TAG,"mModelBeanList.size()=" + mModelBeanList.size()); //初始化LayoutManager layoutManager = new VirtualLayoutManager(this); layoutManager.setRecycleOffset(300); mVLayoutRV.setLayoutManager(layoutManager); //設置回收複用池大小,(若是一屏內相同類型的 View 個數比較多,須要設置一個合適的大小,防止來回滾動時從新建立 View) //針對type=0的item設置了複用池的大小,若是你的頁面有多種type,須要爲每一種類型的分別調整複用池大小參數。 viewPool = new RecyclerView.RecycledViewPool(); mVLayoutRV.setRecycledViewPool(viewPool); //加載數據,經過建立adapter集合實現佈局 delegateAdapter = new DelegateAdapter(layoutManager, false);//必須使用false,實現每個分組的類型不一樣 mVLayoutRV.setAdapter(delegateAdapter); setVLayoutAdapter(); } /**設置適配器*/ private void setVLayoutAdapter() { itemType = 0;//自增長1 if (adapters != null) { adapters.clear(); } else { adapters = new LinkedList<>(); } //根據類型不一樣,採用不一樣的adapter並添加到集合中 for(int i=0; i<mModelBeanList.size(); i++){ String modelName = mModelBeanList.get(i).getModelName(); String type = mModelBeanList.get(i).getType(); ArrayList<ItemBean> itemBeanArrayList = (ArrayList<ItemBean>) mModelBeanList.get(i).getItemDataList(); boolean showModelName = true;//控制是否顯示model,也就是吸頂佈局 if(showModelName){ //stikcy佈局, 能夠配置吸頂或者吸底 //設置各個區域的複用池的大小,由於只有一個元素,因此複用池大小就設置爲1 viewPool.setMaxRecycledViews(itemType++, 1); StickyLayoutHelper stickyLayoutHelper = new StickyLayoutHelper(); StickyTitleAdapter stickyTitleAdapter = new StickyTitleAdapter(this,stickyLayoutHelper,modelName); adapters.add(stickyTitleAdapter); } //========用來判斷分組,展示不一樣的樣式======== //每個item對應一種樣式 switch(type) { case "banner": //通欄佈局——輪播圖 //設置各個區域的複用池的大小,設置子集合的總個數爲複用池大小 viewPool.setMaxRecycledViews(itemType++, itemBeanArrayList.size()); SingleLayoutHelper singleLayoutHelper = new SingleLayoutHelper();//通欄佈局,只會顯示一個組件View singleLayoutHelper.setMargin(0, 0, 0, dip2px(this, 10));//設置外邊距,實現分割效果 BannerLayoutAdapter bannerLayoutAdapter = new BannerLayoutAdapter(this, singleLayoutHelper, itemBeanArrayList); bannerLayoutAdapter.setBannerCallback(new BannerLayoutAdapter.BannerCallback() { @Override public void clickBanner(ItemBean itemBean) { Toast.makeText(MainActivity.this,itemBean.getTitle(),Toast.LENGTH_SHORT).show(); } });//設置自定義回調,用於點擊事件監聽 adapters.add(bannerLayoutAdapter); break; case "hori": //通欄佈局——橫向列表 //設置各個區域的複用池的大小,設置子集合的總個數爲複用池大小 viewPool.setMaxRecycledViews(itemType++, itemBeanArrayList.size()); SingleLayoutHelper horizontalListLayoutHelper = new SingleLayoutHelper();//不使用LinearLayoutHelper horizontalListLayoutHelper.setMargin(0,0,0,dip2px(this, 10));//設置外邊距,實現分割效果 HorizontalListLayoutAdapter horizontalListLayoutAdapter = new HorizontalListLayoutAdapter(this,horizontalListLayoutHelper,itemBeanArrayList); horizontalListLayoutAdapter.setHorizontalListCallback(new HorizontalListLayoutAdapter.HorizontalListCallback() { @Override public void clickHorizontalItem(ItemBean itemBean) { Toast.makeText(MainActivity.this,itemBean.getTitle(),Toast.LENGTH_SHORT).show(); } });//設置自定義回調,用於點擊事件監聽 adapters.add(horizontalListLayoutAdapter); break; case "grid": //九宮格佈局 //設置各個區域的複用池的大小,設置子集合的總個數爲複用池大小 viewPool.setMaxRecycledViews(itemType++, itemBeanArrayList.size()); GridLayoutHelper gridlayoutHelper = new GridLayoutHelper(2);//Grid佈局, 支持橫向的colspan gridlayoutHelper.setAutoExpand(false);//解決單數的時候,最後一張居中顯示的問題 gridlayoutHelper.setMargin(0, 0, 0, dip2px(this, 10));//設置外邊距,實現分割效果 GridLayoutAdapter gridLayoutAdapter = new GridLayoutAdapter(this, gridlayoutHelper, itemBeanArrayList, itemBeanArrayList.size()); //設置自定義回調,用於點擊事件監聽 gridLayoutAdapter.setGridCallback(new GridLayoutAdapter.GridCallback() { @Override public void clickGrid(ItemBean itemBean) { Toast.makeText(MainActivity.this,itemBean.getTitle(),Toast.LENGTH_SHORT).show(); } }); adapters.add(gridLayoutAdapter); break; case "list": //列表佈局(默認佈局) //設置各個區域的複用池的大小,設置子集合的總個數爲複用池大小 viewPool.setMaxRecycledViews(itemType++, itemBeanArrayList.size()); LinearLayoutHelper linearLayoutHelper = new LinearLayoutHelper();//線性佈局 linearLayoutHelper.setMargin(0, 0, 0, dip2px(this, 10));//設置外邊距,實現分割效果 ListLayoutAdapter listLayoutAdapter = new ListLayoutAdapter(this, linearLayoutHelper, itemBeanArrayList, itemBeanArrayList.size()); //設置自定義回調,用於點擊事件監聽 listLayoutAdapter.setListCallback(new ListLayoutAdapter.ListCallback() { @Override public void clickList(ItemBean itemBean) { Toast.makeText(MainActivity.this,itemBean.getTitle(),Toast.LENGTH_SHORT).show(); } }); adapters.add(listLayoutAdapter); break; default: } } delegateAdapter.setAdapters(adapters); } /** * dp轉px * 16dp - 48px * 17dp - 51px*/ public static int dip2px(Context context, float dpValue) { float scale = context.getResources().getDisplayMetrics().density; return (int)((dpValue * scale) + 0.5f); } /*=======================================獲取測試數據==============================================*/ private void getTestDatas(){ String listdata = getStringFromAssert(MainActivity.this,"vlayout.txt"); try { JSONObject listObj = new JSONObject(listdata); JSONArray listArray = listObj.getJSONArray("data"); for(int i=0; i< listArray.length(); i++){ JSONObject itemObj = listArray.getJSONObject(i); ModelBean modelBean = new ModelBean(); modelBean.setModelName(itemObj.getString("modelname")); modelBean.setType(itemObj.getString("type")); JSONArray childArray = itemObj.getJSONArray("data"); List<ItemBean> itemBeanList = new ArrayList<ItemBean>(); for(int j=0; j<childArray.length(); j++){ JSONObject childObj = childArray.getJSONObject(j); ItemBean itemBean = new ItemBean(); itemBean.setId(childObj.getString("id")); itemBean.setImageUrl(childObj.getString("image")); itemBean.setOrder(childObj.getString("order")); itemBean.setTitle(childObj.getString("title")); itemBean.setUrlPath(childObj.getString("url")); itemBeanList.add(itemBean); } modelBean.setItemDataList(itemBeanList); mModelBeanList.add(modelBean); } } catch (JSONException e) { e.printStackTrace(); } } /** * 訪問assets目錄下的資源文件,獲取文件中的字符串 * @param assetsFilePath - 文件的相對路徑,例如:"listitemdata.txt或者"/why/listdata.txt" * @return 內容字符串 * */ public static String getStringFromAssert(Context mContext, String assetsFilePath) { String content = ""; // 結果字符串 try { InputStream is = mContext.getResources().getAssets().open(assetsFilePath);// 打開文件 int ch = 0; ByteArrayOutputStream out = new ByteArrayOutputStream(); // 實現了一個輸出流 while ((ch = is.read()) != -1) { out.write(ch); // 將指定的字節寫入此 byte 數組輸出流 } byte[] buff = out.toByteArray();// 以 byte 數組的形式返回此輸出流的當前內容 out.close(); // 關閉流 is.close(); // 關閉流 content = new String(buff, "UTF-8"); // 設置字符串編碼 } catch (Exception e) { Toast.makeText(mContext, "對不起,沒有找到指定文件!", Toast.LENGTH_SHORT) .show(); } return content; } }
#Vlayout
-keepattributes InnerClasses
-keep class com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx { *; }
-keep class android.support.v7.widget.RecyclerView$LayoutParams { *; }
-keep class android.support.v7.widget.RecyclerView$ViewHolder { *; }
-keep class android.support.v7.widget.ChildHelper { *; }
-keep class android.support.v7.widget.ChildHelper$Bucket { *; }
-keep class android.support.v7.widget.RecyclerView$LayoutManager { *; }