商城首頁(yè)App的編寫(xiě),首先我們要確定我們需要確定一下大框架:

如圖所示,我們可以看到,首先我們需要一個(gè)內(nèi)容根布局,和一個(gè)標(biāo)題欄,之后是RecyclerView,之所以使用RecyclerView,是因?yàn)槲覀兺ㄟ^(guò)聯(lián)網(wǎng)訪(fǎng)問(wèn)需要獲取到實(shí)時(shí)數(shù)據(jù)。可以根據(jù)內(nèi)容自定義我們需要的View,然后添加到RecyclerView中,有多少就能寫(xiě)多了.不用擔(dān)心數(shù)據(jù)會(huì)儲(chǔ)存不下去。好了,基本框架說(shuō)完了,讓我們來(lái)看看如何實(shí)現(xiàn)。
首先是關(guān)于ToolBar的標(biāo)題欄和狀態(tài)欄,因?yàn)槭謾C(jī)本身顯示內(nèi)容界面有限,所以,為了保證用戶(hù)能夠流暢的去使用我們的app,就需要對(duì)標(biāo)題欄和狀態(tài)欄進(jìn)行改動(dòng)。
一.沉浸式狀態(tài)欄:
何為沉浸式狀態(tài)欄,有人可能說(shuō),像餓了嗎,美團(tuán),qq那種狀態(tài)欄和背景色一致,就是沉浸式狀態(tài)欄,其實(shí)不然,這里參考郭霖大神的博客https://blog.csdn.net/guolin_blog/article/details/51763825
何為沉浸式,就是像游戲,電影,電視劇那樣給人帶來(lái)觀(guān)賞體驗(yàn),讓人沉浸其中,不受系統(tǒng)UI的影響,比如像下圖:

像這樣,用戶(hù)不會(huì)被標(biāo)題欄與狀態(tài)欄所干擾,稱(chēng)為沉浸式狀態(tài)欄。
其他的App則是被稱(chēng)作透明狀態(tài)欄這里我簡(jiǎn)單介紹下透明狀態(tài)欄如何實(shí)現(xiàn)
首先現(xiàn)將狀態(tài)欄和標(biāo)題欄隱藏:
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(option);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
效果如下:

當(dāng)然,這也不是沉浸式的,只是隱藏了標(biāo)題欄和狀態(tài)欄,應(yīng)該算是全屏模式吧,引導(dǎo)頁(yè)就可以這么制作。下面我們修改下代碼:
if (Build.VERSION.SDK_INT >= 21) {
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
getWindow().setNavigationBarColor(Color.TRANSPARENT);
getWindow().setStatusBarColor(Color.TRANSPARENT);
}
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
透明狀態(tài)欄只有在5.0的時(shí)候才會(huì)有效果,所以我們需要對(duì)這里進(jìn)行SDK的判斷。接下來(lái)我們使用了SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和SYSTEM_UI_FLAG_LAYOUT_STABLE,注意兩個(gè)Flag必須要結(jié)合在一起使用,表示會(huì)讓?xiě)?yīng)用的主體內(nèi)容占用系統(tǒng)狀態(tài)欄的空間,最后再調(diào)用Window的setStatusBarColor()方法將狀態(tài)欄設(shè)置成透明色就可以了。效果如下:

好了,完成透明狀態(tài)欄以后,我們開(kāi)始編寫(xiě)布局代碼:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorYelloFFDE00" />
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_fragment_home_context"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tool_bar"
android:background="@color/color_rv_bg"
android:clipToPadding="true"
android:descendantFocusability="blocksDescendants"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:requiresFadingEdge="none"
android:scrollbars="none" />
</RelativeLayout>
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.design.widget.CoordinatorLayout>
布局還是很簡(jiǎn)單的,這里不再贅述了,我們所需要的就是從網(wǎng)絡(luò)獲取到j(luò)son數(shù)據(jù),然后再講json轉(zhuǎn)化為我們需要的信息展示到用戶(hù)UI界面上
所以我們需要對(duì)RecyclerView進(jìn)行定制。這里介紹阿里的開(kāi)源框架VirtualLayout。
VirtualLayout是什么:
VirtualLayout是一個(gè)針對(duì)RecyclerView的LayoutManager擴(kuò)展, 主要提供一整套布局方案和布局間的組件復(fù)用的問(wèn)題。
VirtualLayout有什么作用
通過(guò)定制化的LayoutManager,接管整個(gè)RecyclerView的布局邏輯;LayoutManager管理了一系列LayoutHelper,LayoutHelper負(fù)責(zé)具體布局邏輯實(shí)現(xiàn)的地方;每一個(gè)LayoutHelper負(fù)責(zé)頁(yè)面某一個(gè)范圍內(nèi)的組件布局;不同的LayoutHelper可以做不同的布局邏輯,因此可以在一個(gè)RecyclerView頁(yè)面里提供異構(gòu)的布局結(jié)構(gòu),這就能比系統(tǒng)自帶的LinearLayoutManager、GridLayoutManager等提供更加豐富的能力。同時(shí)支持?jǐn)U展LayoutHelper來(lái)提供更多的布局能力。默認(rèn)通用布局實(shí)現(xiàn),解耦所有的View和布局之間的關(guān)系: Linear, Grid, 吸頂, 浮動(dòng), 固定位置等。
① LinearLayoutHelper: 線(xiàn)性布局
②GridLayoutHelper: Grid布局, 支持橫向的colspan
③FixLayoutHelper: 固定布局,始終在屏幕固定位置顯示
④ScrollFixLayoutHelper: 固定布局,但之后當(dāng)頁(yè)面滑動(dòng)到該圖片區(qū)域才 顯示, 可以用來(lái)做返回頂部或其他書(shū)簽等
⑤FloatLayoutHelper:浮動(dòng)布局,可以固定顯示在屏幕上,但用戶(hù)可以拖拽其位置
⑥ColumnLayoutHelper: 欄格布局,都布局在一排,可以配置不同列之間的寬度比值
⑦SingleLayoutHelper: 通欄布局,只會(huì)顯示一個(gè)組件View
⑧OnePlusNLayoutHelper: 一拖N布局,可以配置1-5個(gè)子元素
⑨StickyLayoutHelper: stikcy布局, 可以配置吸頂或者吸底
⑩StaggeredGridLayoutHelper:瀑布流布局,可配置間隔高度/寬度
上述默認(rèn)實(shí)現(xiàn)里可以大致分為兩類(lèi):一是非fix類(lèi)型布局,像線(xiàn)性、Grid、欄格等,它們的特點(diǎn)是布局在整個(gè)頁(yè)面流里,隨頁(yè)面滾動(dòng)而滾動(dòng);另一類(lèi)就是fix類(lèi)型的布局,它們的子節(jié)點(diǎn)往往不隨頁(yè)面滾動(dòng)而滾動(dòng)。
所有除布局外的組件復(fù)用,VirtualLayout將用來(lái)管理大的模塊布局組合,擴(kuò)展了RecyclerView,使得同一RecyclerView內(nèi)的組件可以復(fù)用,減少View的創(chuàng)建和銷(xiāo)毀過(guò)程。
VirtualLayout的使用
① 初始化LayoutManger
mlayoutManager = new VirtualLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mlayoutManager);
②設(shè)置回收復(fù)用池大小,(如果一屏內(nèi)相同類(lèi)型的 View 個(gè)數(shù)比較多,需要設(shè)置一個(gè)合適的大小,防止來(lái)回滾動(dòng)時(shí)重新創(chuàng)建 View):
mviewPool = new RecyclerView.RecycledViewPool();
mRecyclerView.setRecycledViewPool(mviewPool);
mviewPool.setMaxRecycledViews(0, 20);
這里,因?yàn)榻缑鏄邮奖容^多,所以,我們自定義Adapter去繼承DelegateAdapter.adapter,按照需求去編寫(xiě)我們的界面
public class GeneralVLayoutAdapter extends DelegateAdapter.Adapter<GeneralVLayoutAdapter.MainViewHolder> {
private Context mContext;
private LayoutHelper helper;
private VirtualLayoutManager.LayoutParams params;
private int mCount = 0;
public GeneralVLayoutAdapter(Context context, LayoutHelper helper,
VirtualLayoutManager.LayoutParams params, int count) {
mContext = context;
this.helper = helper;
this.params = params;
mCount = count;
}
public GeneralVLayoutAdapter(Context context, LayoutHelper helper, int count) {
this(context, helper, null, count);
}
@Override
public LayoutHelper onCreateLayoutHelper() {
return helper;
}
@NonNull
@Override
public MainViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return null;
}
@Override
public void onBindViewHolder(@NonNull MainViewHolder mainViewHolder, int i) {
if (params != null) {
mainViewHolder.itemView.setLayoutParams(new VirtualLayoutManager.LayoutParams(params));
}
}
@Override
public int getItemCount() {
return mCount;
}
public class MainViewHolder extends RecyclerView.ViewHolder {
public MainViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}
這段代碼也很簡(jiǎn)單我們需要關(guān)注一下幾個(gè)方法
onCreateViewHolder 這個(gè)方法需要我們?nèi)ブ貙?xiě),因?yàn)槊總€(gè)布局對(duì)應(yīng)的viewHolder都不一樣,所以需要我們?cè)诓煌牟季掷锶ブ貙?xiě)對(duì)應(yīng)的ViewHolder。
onBindViewHolder 這個(gè)方法也需要我們?nèi)ブ貙?xiě),對(duì)應(yīng)著ViewHolder的子項(xiàng)的數(shù)據(jù)進(jìn)行賦值,
getItemCount 這個(gè)方法更簡(jiǎn)單了,它相當(dāng)于告訴了RecyclerView有多少個(gè)子項(xiàng),直接返回?cái)?shù)據(jù)源的長(zhǎng)度。因?yàn)镽ecyclerView已經(jīng)為我們封裝好了ViewHolder,所以我們的MainViewHolder直接繼承RecyclerView的ViewHolder,這沒(méi)啥好說(shuō)的。
private DelegateAdapter mdelegateAdapter;
private List<DelegateAdapter.Adapter> madapters = new LinkedList<>();
mdelegateAdapter = new DelegateAdapter(mlayoutManager, false);
mRecyclerView.setAdapter(mdelegateAdapter);
這里的代碼需要我們綜合去看,因?yàn)橐粋€(gè)布局就對(duì)應(yīng)著一個(gè)adapter,所以我們需要一個(gè)集合去添加這些adapter.下面會(huì)在代碼展示,就不詳細(xì)說(shuō)明了。
接下來(lái)我們要準(zhǔn)備工具,將獲取到的json數(shù)據(jù)轉(zhuǎn)化為UI界面,我們需要使用到OkHttp
相信很多人都使用過(guò),因?yàn)楸萭oogle推送的HttpURLConnection連接要快捷簡(jiǎn)便的多。使用方法如下,首先我們需要添加庫(kù)依賴(lài),編輯Gradle文件
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
同時(shí)也要添加權(quán)限編輯manifest文件
<uses-permission android:name="android.permission.INTERNET" />
這樣我們就能訪(fǎng)問(wèn)網(wǎng)絡(luò),獲取JSON數(shù)據(jù)了
public class OkHttpUtil {
private static OkHttpUtil okHttpUtil = null;
private OkHttpClient mHttpClient;
private Request mRequest;
public OkHttpUtil() {
}
public static OkHttpUtil getInstance() {
if (okHttpUtil == null) {
synchronized (OkHttpUtil.class) {
if (okHttpUtil == null) {
okHttpUtil = new OkHttpUtil();
}
}
}
return okHttpUtil;
}
public void startGet(String url, final OnNetResultListener listener) {
mHttpClient = new OkHttpClient();
mRequest = new Request.Builder().url(url).build();
mHttpClient.newCall(mRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
listener.OnFailureListener(e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
listener.OnSuccessListener(response.body().string());
}
});
}
public void startPost(String url, String phone, String username, String password, String data,
final OnNetResultListener listener) {
mHttpClient = new OkHttpClient();
final RequestBody requestBody = new FormBody.Builder()
.add("phone", phone)
.add("username", username)
.add("password", password)
.add("regdate", data)
.build();
mRequest = new Request.Builder().url(url).post(requestBody).build();
mHttpClient.newCall(mRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
listener.OnFailureListener(e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
listener.OnSuccessListener(response.body().string());
}
});
}
}
這是一個(gè)封裝好的工具類(lèi),思路也很簡(jiǎn)單,首先我們需要一個(gè)okHttpClient的實(shí)例,因?yàn)槲覀冃枰L(fǎng)問(wèn)網(wǎng)絡(luò),所以我們也需要訪(fǎng)問(wèn)URL地址,如果要訪(fǎng)問(wèn),自然就需要Request對(duì)象,需要注意的是我們的訪(fǎng)問(wèn)請(qǐng)求是GET還是Post,其實(shí)GET和Post差不多,稍微復(fù)雜點(diǎn),Post需要構(gòu)建出一個(gè)RequestBody對(duì)象,來(lái)存儲(chǔ)我們需要提交的參數(shù),然后用post的方法將參數(shù)提交,接下來(lái),就是和Get一樣的操作。,好了,這樣我們就成功的發(fā)送了請(qǐng)求。如果請(qǐng)求成功,我們會(huì)得到數(shù)據(jù)源,這個(gè)時(shí)候我們就需要對(duì)其解析,傳送數(shù)據(jù)分為XML和JSON兩種格式,這里我們主要說(shuō)下如何對(duì)JSON數(shù)據(jù)的解析,XML暫且不做考慮,主要說(shuō)一下如何解析JSON數(shù)據(jù)
public class JsonUtil {
private static final int HOME_COODS_INFO = 0;
private static final int BANK_CARD_INFO = 1;
private static final int CLASSIFY_GOODS_INFO = 2;
private static final int MALL_GOODS_INFO = 3;
private static final int GOODS_DETAILS_INFO = 4;
public List getDataFromJson(String json, int type) {
List<HomeSortInfo> homeSortInfos = new ArrayList<>();
List<HomeSortItemfo> homeSortItemfos = new ArrayList<>();
JSONObject mJsonObject;
JSONArray mJsonArray;
try {
if (type == HOME_COODS_INFO) {
//首頁(yè)的商品信息
mJsonObject = new JSONObject(json);
mJsonArray = mJsonObject.getJSONArray("home_sort");
for (int i = 0; i < mJsonArray.length(); i++) {
JSONObject jsonObject = (JSONObject) mJsonArray.get(i);
String title = jsonObject.getString("title");
String sortImageUrl = jsonObject.getString("sortImageUrl");
JSONArray jsonArray = jsonObject.getJSONArray("goods");
for (int j = 0; j < jsonArray.length(); j++) {
JSONObject jsonObject1 = (JSONObject) jsonArray.get(j);
String id = jsonObject1.getString("id");
String goodsImageUrl = jsonObject1.getString("goodsImageUrl");
homeSortItemfos.add(new HomeSortItemfo(id, goodsImageUrl));
}
homeSortInfos.add(new HomeSortInfo(title, sortImageUrl, homeSortItemfos));
}
LogUtil.d("LF1234", "homeSortInfos" + homeSortInfos);
return homeSortInfos;
} else if (type == BANK_CARD_INFO) {
} else if (type == CLASSIFY_GOODS_INFO) {
List<ClassifyGoodsInfo> classifyGoodsInfos = new ArrayList<>();
mJsonObject = new JSONObject(json);
mJsonArray = mJsonObject.getJSONArray("classifyTitle");
for (int i = 0; i < mJsonArray.length(); i++) {
JSONObject jsonObject = (JSONObject) mJsonArray.get(i);
String title = jsonObject.getString("title");
String headerImageUrl = jsonObject.getString("headerImageUrl");
String subtitle1 = jsonObject.getString("subTitle1");
String subtitle2 = jsonObject.getString("suTitle");
JSONArray jsonArray = ((JSONObject) mJsonArray.get(i)).getJSONArray("gridImageUrls1");
List<ClassifyGridInfo> mGridInfos1 = new ArrayList<>();
List<ClassifyGridInfo> mGridInfos2 = new ArrayList<>();
for (int j = 0; j < jsonArray.length(); j++) {
JSONObject jsonObject1 = (JSONObject) jsonArray.get(j);
int id = jsonObject1.getInt("id");
String desc = jsonObject1.getString("desc");
String imageUrl = jsonObject1.getString("iamgeUrl");
mGridInfos1.add(new ClassifyGridInfo(id, desc, imageUrl));
}
JSONArray jsonArray1 = ((JSONObject) mJsonArray.get(i)).getJSONArray("gridImageUrls2");
for (int j = 0; j < jsonArray1.length(); j++) {
JSONObject jsonObject1 = (JSONObject) jsonArray1.get(j);
int id = jsonObject1.getInt("id");
String desc = jsonObject1.getString("desc");
String imageUrl = jsonObject1.getString("iamgeUrl");
mGridInfos2.add(new ClassifyGridInfo(id, desc, imageUrl));
}
classifyGoodsInfos.add(new ClassifyGoodsInfo(title, headerImageUrl, subtitle1, subtitle2, mGridInfos1, mGridInfos2));
}
return classifyGoodsInfos;
} else if (type == MALL_GOODS_INFO) {
List<MallPagerInfo> mallPagerInfos = new ArrayList<>();
List<BannerInfo> bannerInfos = new ArrayList<>();
List<GridInfo> gridInfos = new ArrayList<>();
List<BannerInfo> goodsInfos = new ArrayList<>();
List<MallGoodsInfo> mallGoodsInfos = new ArrayList<>();
mJsonObject = new JSONObject(json);
String singleImageUrl = mJsonObject.getString("single_image");
mJsonArray = mJsonObject.getJSONArray("banners");
for (int i = 0; i < mJsonArray.length(); i++) {
JSONObject jsonObject = (JSONObject) mJsonArray.get(i);
bannerInfos.add(new BannerInfo(jsonObject.getString("banner_url")));
}
JSONArray jsonArrayGrid = mJsonObject.getJSONArray("classifyGridItems");
for (int i = 0; i < jsonArrayGrid.length(); i++) {
JSONObject jsonObject = (JSONObject) jsonArrayGrid.get(i);
gridInfos.add(new GridInfo(
jsonObject.getString("desc"),
jsonObject.getString("grid_url")));
}
JSONArray jsonArrayGoods = mJsonObject.getJSONArray("four_goods_image");
for (int i = 0; i < jsonArrayGoods.length(); i++) {
JSONObject jsonObject = (JSONObject) jsonArrayGoods.get(i);
goodsInfos.add(new BannerInfo(jsonObject.getString("four_image_url")));
}
JSONArray jsonArrayHotSorts = mJsonObject.getJSONArray("hotSort");
for (int i = 0; i < jsonArrayHotSorts.length(); i++) {
List<MallGoodsItemInfo> mallGoodsItemInfos = new ArrayList<>();
JSONObject jsonObject = (JSONObject) jsonArrayHotSorts.get(i);
String headerImageUrl = jsonObject.getString("headerBigImage");
JSONArray jsonArray = jsonObject.getJSONArray("threeGoods");
for (int j = 0; j < jsonArray.length(); j++) {
JSONObject jsonObject1 = (JSONObject) jsonArray.get(j);
mallGoodsItemInfos.add(new MallGoodsItemInfo(
jsonObject1.getString("goodsItemImage"),
jsonObject1.getString("desc"),
jsonObject1.getDouble("singePrice"),
jsonObject1.getInt("numPeriods"),
jsonObject1.getDouble("price")));
}
mallGoodsInfos.add(new MallGoodsInfo(headerImageUrl, mallGoodsItemInfos));
}
JSONArray jsonArrayReco = mJsonObject.getJSONArray("recommends_goods");
List<RecommendGoodsInfo> recommendGoodsInfoList = new ArrayList<>();
for (int i = 0; i < jsonArrayReco.length(); i++) {
JSONObject jsonObject = (JSONObject) jsonArrayReco.get(i);
recommendGoodsInfoList.add(new RecommendGoodsInfo(
jsonObject.getString("imageUrl"),
jsonObject.getString("desc"),
jsonObject.getDouble("singlePrice"),
jsonObject.getInt("periods"),
jsonObject.getDouble("totalPrice"),
jsonObject.getString("rate")));
}
mallPagerInfos.add(new MallPagerInfo(
bannerInfos,
gridInfos,
singleImageUrl,
goodsInfos,
mallGoodsInfos,
recommendGoodsInfoList));
return mallPagerInfos;
} else if (type == GOODS_DETAILS_INFO) {
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
}
這是我自己的解析工具類(lèi)使用的是JSONObject解析,很簡(jiǎn)單,我們首先創(chuàng)建數(shù)據(jù)模型bean,比如說(shuō),姓名,年齡,性別,可能都是在同一段節(jié)點(diǎn)的同一數(shù)據(jù)源中,所以我們用bean的模式,然后創(chuàng)建各屬性的get和set方法。再用list集合去添加,然后在遍歷集合的形式,將解析的數(shù)據(jù)展示到我們的界面上。下面貼出bean的代碼
public class ClassifyGridInfo {
private int id;
private String name;
private String imageUrl;
public ClassifyGridInfo(int id, String name, String imageUrl) {
this.id = id;
this.name = name;
this.imageUrl = imageUrl;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
}
再貼出JSON數(shù)據(jù)源
[{"id":"1","name":"**","imageUrl":"****"},{"id":"2","name":"**","imageUrl":"****"},
{"id":"1","name":"**","imageUrl":"****"},.....
]
這里只是舉例,我們通過(guò)JSONArray將數(shù)據(jù)源轉(zhuǎn)化成JSONObject,然后通過(guò)JSONObject去獲取每一個(gè)元素的屬性值,這里就不在贅述了。
好了,有了這些工具,下面我們來(lái)說(shuō)一下關(guān)于展示界面的UI設(shè)計(jì)。
一、輪播圖
輪播圖是一種很常見(jiàn)的動(dòng)態(tài)UI界面,如下圖所示:

這是網(wǎng)站上的輪播圖,用戶(hù)對(duì)于輪播圖的反映褒貶不一,但是不可否認(rèn),它能夠很直觀(guān)的給用戶(hù)以需求,介紹自己產(chǎn)品的特色,如何實(shí)現(xiàn)輪播圖,將是我們的重點(diǎn),我會(huì)使用RecyclerView去實(shí)現(xiàn)輪播效果。
首先,我們需要的是自定義控件屬性,對(duì),你沒(méi)有聽(tīng)錯(cuò),是自定義控件屬性,因?yàn)橛袝r(shí)候控件屬性不能滿(mǎn)足我們的需求,所以,我們需要自定義控件屬性,首先,我們需要在res文件夾下values文件下新建一個(gè)attrs文件夾,用于存放我們自定義控件的屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RecyclerViewBanner">
<attr name="rvb_interval" format="integer" />
<attr name="rvb_showIndicator" format="boolean" />
<attr name="rvb_indicatorSelectedSrc" format="color|reference" />
<attr name="rvb_indicatorUnSelectedSrc" format="color|reference" />
<attr name="rvb_indicatorSize" format="dimension|reference" />
<attr name="rvb_indicatorSpace" format="dimension|reference" />
<attr name="rvb_indicatorMargin" format="dimension|reference" />
<attr name="rvb_indicatorGravity" format="enum">
<enum name="left" value="0" />
<enum name="center" value="1" />
<enum name="right" value="2" />
</attr>
<attr name="rvb_autoPlaying" format="boolean" />
</declare-styleable>
下面我們來(lái)一個(gè)個(gè)分析一下
- <declare-styleable name=“”/> 這是一個(gè)資源文件的索引名,類(lèi)似Id,在代碼編寫(xiě)時(shí)候,能夠很快確定你的自定義控件屬性位置。
-
<attr name= "" format = " "/>attr name 是申明你想自定義控件的屬性的名稱(chēng) format 則是設(shè)定控件屬性類(lèi)型
color:顏色值。boolean:布爾值。dimension:尺寸值。float:浮點(diǎn)值。integer:整型值。string:字符串。fraction:百分?jǐn)?shù)。enum:枚舉值。flag:位或運(yùn)算。reference:參考某一資源ID。
現(xiàn)在我們新建一個(gè)類(lèi)去繼承FragmentLayout;
public class RecyclerViewBanner extends FrameLayout {
private static final int DEFAULT_SELECT_COLOR = 0xffffffff;//選中時(shí)的顏色
private static final int DEFAULT_UNSELECTED_COLOR = 0X50ffffff;//未來(lái)選中時(shí)的顏色
private RecyclerView mRecyclerView;
private RecyclerViewAdapter adapter;
private LinearLayout mLinearLayout;
private OnRvBannerClickListener listener;
private OnSwitchRvBannerListener onSwitchRvBannerListener;
private boolean isPlaying;//是否播放
private int startX, startY, currentIndex;//從X軸開(kāi)始的位置,從Y軸開(kāi)始的位置
private int mSize;//大小
private int mSpace;//間距
private int mInterval;//時(shí)間間隔
private int margin;//距離外邊距的距離
private int gravity;//位置
private boolean isShowIndicator;//是否顯示指示器
private boolean isAutoPlaying;//是否自動(dòng)播放
private boolean isTouched;//是否觸摸
private List<Object> mData = new ArrayList<>();
private Drawable mSelectedDrawable;//選中時(shí)背景
private Drawable mUnSelectedDrawable;//未選中時(shí)的背景
private Drawable selectedSrc;//選中時(shí)的資源
private Drawable unSelectedSrc;//未選中時(shí)的資源
private Handler mHandler = new Handler();
private Runnable playTask = new Runnable() {
@Override
public void run() {
mRecyclerView.smoothScrollToPosition(++currentIndex);//讓RecyclerView平滑到指定的位置
if (isShowIndicator) {
changePoint();//改變圓點(diǎn)指示器的顯示位置
}
mHandler.postDelayed(this, mInterval);//設(shè)置定時(shí)器
}
};
public RecyclerViewBanner(@NonNull Context context) {
super(context);
init(context, null);
}
public RecyclerViewBanner(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public RecyclerViewBanner(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
}
private void init(final Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RecyclerViewBanner);
mInterval = typedArray.getInt(R.styleable.RecyclerViewBanner_rvb_interval, 3000);
isShowIndicator = typedArray.getBoolean(R.styleable.RecyclerViewBanner_rvb_showIndicator,
true);
isAutoPlaying = typedArray.getBoolean(R.styleable.RecyclerViewBanner_rvb_autoPlaying,
true);
selectedSrc = typedArray.getDrawable(R.styleable.RecyclerViewBanner_rvb_indicatorSelectedSrc);
unSelectedSrc = typedArray.getDrawable(R.styleable.RecyclerViewBanner_rvb_indicatorUnSelectedSrc);
mSize = typedArray.getDimensionPixelSize(R.styleable.RecyclerViewBanner_rvb_indicatorSize,
0);
mSpace = typedArray.getDimensionPixelSize(R.styleable.RecyclerViewBanner_rvb_indicatorSpace,
dp2px(4));
margin = typedArray.getDimensionPixelSize(R.styleable.RecyclerViewBanner_rvb_indicatorMargin,
dp2px(10));
int g = typedArray.getInt(R.styleable.RecyclerViewBanner_rvb_indicatorGravity,
1);
switch (g) {
case 0:
gravity = Gravity.START;
break;
case 1:
gravity = Gravity.CENTER;
break;
case 2:
gravity = Gravity.END;
break;
default:
break;
}
if (selectedSrc == null) {
mSelectedDrawable = gradientDrawable(DEFAULT_SELECT_COLOR);
} else {
if (selectedSrc instanceof ColorDrawable) {
mSelectedDrawable = gradientDrawable(((ColorDrawable) selectedSrc).getColor());
} else {
mSelectedDrawable = selectedSrc;
}
}
if (unSelectedSrc == null) {
mUnSelectedDrawable = gradientDrawable(DEFAULT_UNSELECTED_COLOR);
} else {
if (unSelectedSrc instanceof ColorDrawable) {
mUnSelectedDrawable = gradientDrawable(((ColorDrawable) unSelectedSrc).getColor());
} else {
mUnSelectedDrawable = unSelectedSrc;
}
}
typedArray.recycle();//回收 TypedArray,用于后續(xù)調(diào)用時(shí)可復(fù)用之。當(dāng)調(diào)用該方法后,不能再操作該變量。
mRecyclerView = new RecyclerView(context);
mLinearLayout = new LinearLayout(context);
new PagerSnapHelper().attachToRecyclerView(mRecyclerView);//輔助Recycler進(jìn)行滾動(dòng)對(duì)齊
adapter = new RecyclerViewAdapter();
mRecyclerView.setAdapter(adapter);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
int first = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
int last = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();
if (currentIndex != (first + last) / 2) {
currentIndex = (first + last) / 2;
changePoint();
}
}
}
});
mLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
mLinearLayout.setGravity(Gravity.CENTER);
LayoutParams vpLayoutParams = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
LayoutParams linearLayoutParams = new LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
linearLayoutParams.gravity = Gravity.BOTTOM | gravity;
linearLayoutParams.setMargins(margin, margin, margin, margin);
addView(mRecyclerView, vpLayoutParams);
addView(mLinearLayout, linearLayoutParams);
}
/**
* 設(shè)置間隔時(shí)間
*
* @param mInterval
*/
public void setmInterval(int mInterval) {
this.mInterval = mInterval;
}
/**
* 設(shè)置是否指示器導(dǎo)航點(diǎn)
*
* @param showIndicator
*/
public void setShowIndicator(boolean showIndicator) {
isShowIndicator = showIndicator;
}
/**
* 設(shè)置是否禁止?jié)L動(dòng)播放
*
* @param autoPlaying
*/
public void setAutoPlaying(boolean autoPlaying) {
isAutoPlaying = autoPlaying;
}
/**
* 設(shè)置是否自動(dòng)播放(上鎖)
*
* @param playing 開(kāi)始播放
*/
private synchronized void setPlaying(boolean playing) {
if (isAutoPlaying) {
if (!isPlaying && playing && adapter != null && adapter.getItemCount() > 2) {
mHandler.postDelayed(playTask, mInterval);
isPlaying = true;
} else if (isPlaying && !playing) {
mHandler.removeCallbacksAndMessages(null);
isPlaying = false;
}
}
}
//手指觸摸事件
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = (int) ev.getX();
startY = (int) ev.getY();
//阻止父層View攔截事件
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) ev.getX();
int moveY = (int) ev.getY();
int disX = moveX - startX;
int disY = moveY - startY;
boolean hasMoved = 2 * Math.abs(disX) > Math.abs(disY);
getParent().requestDisallowInterceptTouchEvent(hasMoved);
if (hasMoved) {
setPlaying(false);
}
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
if (!isPlaying) {
isTouched = true;
setPlaying(true);
}
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setPlaying(true);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
setPlaying(false);
}
@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
if (visibility == GONE || visibility == INVISIBLE) {
// 停止輪播
setPlaying(false);
} else if (visibility == VISIBLE) {
// 開(kāi)始輪播
setPlaying(true);
}
super.onVisibilityChanged(changedView, visibility);
}
//創(chuàng)建默認(rèn)指示器
private GradientDrawable gradientDrawable(int color) {
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setSize(dp2px(6), dp2px(6));
gradientDrawable.setCornerRadius(dp2px(6));
gradientDrawable.setColor(color);
return gradientDrawable;
}
/**
* 指示器整體由數(shù)據(jù)列表容量數(shù)量的AppCompatImageView均勻分布在一個(gè)橫向的LinearLayout中構(gòu)成
* 使用AppCompatImageView的好處是在Fragment中也使用Compat相關(guān)屬性
*/
private void createIndicators() {
mLinearLayout.removeAllViews();//先動(dòng)態(tài)移除所有的View
for (int i = 0; i < mData.size(); i++) {
ImageView imageView = new ImageView(getContext());
LinearLayout.LayoutParams indicatorsParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
indicatorsParams.leftMargin = mSpace / 2;
indicatorsParams.rightMargin = mSpace / 2;
if (mSize >= dp2px(4)) {
indicatorsParams.width = indicatorsParams.height = mSize;
} else {
imageView.setMinimumHeight(dp2px(2));
imageView.setMinimumWidth(dp2px(2));
}
imageView.setImageDrawable(i == 0 ? mSelectedDrawable : mUnSelectedDrawable);
mLinearLayout.addView(imageView, indicatorsParams);
}
}
//將int值轉(zhuǎn)化為dp值
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
}
/**
* 設(shè)置輪播數(shù)據(jù)
*
* @param data
*/
public void setRvBannerData(List data) {
setPlaying(false);
mData.clear();
if (null != data) {
mData.addAll(data);
}
if (mData.size() > 1) {
currentIndex = mData.size();
adapter.notifyDataSetChanged();
mRecyclerView.scrollToPosition(currentIndex);
if (isShowIndicator) {
createIndicators();
}
setPlaying(true);
} else {
currentIndex = 0;
adapter.notifyDataSetChanged();
}
}
private void changePoint() {
if (mLinearLayout != null && mLinearLayout.getChildCount() > 0) {
for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
((ImageView) mLinearLayout.getChildAt(i)).setImageDrawable(
i == currentIndex % mData.size() ? mSelectedDrawable : mUnSelectedDrawable);
}
}
}
class RecyclerViewAdapter extends RecyclerView.Adapter {
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
SimpleDraweeView simpleDraweeView = new SimpleDraweeView(viewGroup.getContext());
RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
simpleDraweeView.setLayoutParams(layoutParams);
simpleDraweeView.setId(R.id.rvb_banner_imageView_id);
simpleDraweeView.setScaleType(ImageView.ScaleType.CENTER_CROP);
simpleDraweeView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.OnClick(currentIndex % mData.size());
}
}
});
return new RecyclerView.ViewHolder(simpleDraweeView) {
};
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
SimpleDraweeView draweeView = viewHolder.itemView.findViewById(R.id.rvb_banner_imageView_id);
if (onSwitchRvBannerListener != null) {
onSwitchRvBannerListener.switchBanner(i % mData.size(), draweeView);
}
}
@Override
public int getItemCount() {
return mData == null ? 0 : mData.size() < 2 ? mData.size() : Integer.MAX_VALUE;
}
}
public void setListener(OnRvBannerClickListener listener) {
this.listener = listener;
}
public void setOnSwitchRvBannerListener(OnSwitchRvBannerListener onSwitchRvBannerListener) {
this.onSwitchRvBannerListener = onSwitchRvBannerListener;
}
private class PagerSnapHelper extends LinearSnapHelper {
@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
//獲取到將要滾動(dòng)的位置targetPos
int targetPos = super.findTargetSnapPosition(layoutManager, velocityX, velocityY);
//找到與之最近的view
View currentView = findSnapView(layoutManager);
if (targetPos != RecyclerView.NOT_FOCUSABLE && null != currentView) {
//獲取最近View的位置currentPos
int currentPos = layoutManager.getPosition(currentView);
int first = ((LinearLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition();
int last = ((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition();
//如果滑動(dòng)的位置<最近view的位置,最近的view的位置對(duì)齊最后一個(gè)可見(jiàn)view的位置
//如果滑動(dòng)的位置>最近view的位置,最近的view的位置對(duì)齊第一個(gè)可見(jiàn)的View的位置
currentPos = targetPos < currentPos ? last : (targetPos > currentPos ? first : currentPos);
//如果滑動(dòng)的位置<最后一個(gè)可見(jiàn)view的位置,將要滑動(dòng)的位置對(duì)齊第一個(gè)view
//如果滑動(dòng)的位置>第一個(gè)可見(jiàn)view的位置,將要滑動(dòng)的位置對(duì)齊最后一個(gè)view
targetPos = targetPos < currentPos ? currentPos - 1 : (
targetPos > currentPos ? currentPos + 1 : currentPos);
}
return targetPos;
}
}
我們繼承FrameLayout需要重寫(xiě)他的三個(gè)構(gòu)造方法
- public RecyclerViewBanner(@NonNull Context context) {
super(context);
init(context, null);
} - public RecyclerViewBanner(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
} -
public RecyclerViewBanner(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
一般來(lái)說(shuō),前兩個(gè)構(gòu)造函數(shù)就足夠,第三個(gè)構(gòu)造函數(shù)的第三個(gè)參數(shù)是用來(lái)設(shè)置默認(rèn)的style樣式,目前不考慮,傳入?yún)?shù)為0即可。我們需要注意的是AttributeSet attrs這個(gè)參數(shù),它就是需要我們傳入的自定義控件屬性的參數(shù)。我們通過(guò)TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RecyclerViewBanner);獲取到當(dāng)前自定義控件屬性集合,然后對(duì)應(yīng)編寫(xiě)默認(rèn)屬性。輪播圖肯定是圖片加指示器,有多少圖片,就應(yīng)該對(duì)應(yīng)多少個(gè)指示器,首先我們先編寫(xiě)RecyclerView容器。
mRecyclerView = new RecyclerView(context);
mLinearLayout = new LinearLayout(context);
new PagerSnapHelper().attachToRecyclerView(mRecyclerView);//輔助Recycler進(jìn)行滾動(dòng)對(duì)齊
adapter = new RecyclerViewAdapter();
mRecyclerView.setAdapter(adapter);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
int first = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
int last = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();
if (currentIndex != (first + last) / 2) {
currentIndex = (first + last) / 2;
changePoint();
}
}
}
});
mLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
mLinearLayout.setGravity(Gravity.CENTER);
LayoutParams vpLayoutParams = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
LayoutParams linearLayoutParams = new LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
linearLayoutParams.gravity = Gravity.BOTTOM | gravity;
linearLayoutParams.setMargins(margin, margin, margin, margin);
addView(mRecyclerView, vpLayoutParams);
addView(mLinearLayout, linearLayoutParams);
這段代碼我們要重點(diǎn)是new PagerSnapHelper().attachToRecyclerView(mRecyclerView);//輔助Recycler進(jìn)行滾動(dòng)對(duì)齊和對(duì)RecyclerView的滾動(dòng)監(jiān)聽(tīng),第一個(gè)方法是通過(guò)對(duì)滑動(dòng)速度定位圖片位置這里推薦大家去看這篇簡(jiǎn)書(shū)http://www.itdecent.cn/p/e54db232df62
第二個(gè)方法是防止RecyclerView停止滑動(dòng)后,圖片位置產(chǎn)生偏差,用于修正。接下來(lái)就需要編寫(xiě)adapter,這個(gè)相信大家已經(jīng)非常熟練了。
class RecyclerViewAdapter extends RecyclerView.Adapter {
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
SimpleDraweeView simpleDraweeView = new SimpleDraweeView(viewGroup.getContext());
RecyclerView.LayoutParams layoutParams = new RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
simpleDraweeView.setLayoutParams(layoutParams);
simpleDraweeView.setId(R.id.rvb_banner_imageView_id);
simpleDraweeView.setScaleType(ImageView.ScaleType.CENTER_CROP);
simpleDraweeView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.OnClick(currentIndex % mData.size());
}
}
});
return new RecyclerView.ViewHolder(simpleDraweeView) {
};
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
SimpleDraweeView draweeView = viewHolder.itemView.findViewById(R.id.rvb_banner_imageView_id);
if (onSwitchRvBannerListener != null) {
onSwitchRvBannerListener.switchBanner(i % mData.size(), draweeView);
}
}
@Override
public int getItemCount() {
return mData == null ? 0 : mData.size() < 2 ? mData.size() : Integer.MAX_VALUE;
}
}
這段代碼也沒(méi)有啥難點(diǎn),直接使用了simpleDraweeView去填充RecyclerView布局就行了。
有人問(wèn)了,如何實(shí)現(xiàn)輪播,這個(gè)還是很簡(jiǎn)單的,只需要利用handler定時(shí)異步操作就行了,我們還需要注意的一個(gè)地方是對(duì)于觸摸的監(jiān)聽(tīng)
//手指觸摸事件
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = (int) ev.getX();
startY = (int) ev.getY();
//阻止父層View攔截事件
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) ev.getX();
int moveY = (int) ev.getY();
int disX = moveX - startX;
int disY = moveY - startY;
boolean hasMoved = 2 * Math.abs(disX) > Math.abs(disY);
getParent().requestDisallowInterceptTouchEvent(hasMoved);
if (hasMoved) {
setPlaying(false);
}
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
if (!isPlaying) {
isTouched = true;
setPlaying(true);
}
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
對(duì)應(yīng)的觸摸事件是按下:ACTION_DOWN,滑動(dòng):ACTION_MOVE,抬起:ACTION_UP,結(jié)束:ACTION_CANCEL,根據(jù)這些觸摸事件,判定輪播圖是否停止或開(kāi)始自動(dòng)輪播。
這樣,我們的輪播圖就編寫(xiě)完成了。我們?nèi)ragment添加一下吧
SingleLayoutHelper singleLayoutHelper = new SingleLayoutHelper();
GeneralVLayoutAdapter bannerAdapter = new GeneralVLayoutAdapter(
getContext(), singleLayoutHelper, 1) {
@NonNull
@Override
public MainViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(getContext()).inflate(
R.layout.home_pager_bannner_layout, viewGroup, false);
return new MainViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MainViewHolder mainViewHolder, int i) {
super.onBindViewHolder(mainViewHolder, i);
mRecyclerViewBanner = mainViewHolder.itemView.findViewById(R.id.rvb_home_banner);
mBannerInfos = new ArrayList<>();
mBannerInfos.add(new BannerInfo("https://i.loli.net/2018/04/06/5ac733bc51d0a.png"));
mBannerInfos.add(new BannerInfo("https://i.loli.net/2018/04/06/5ac735502effe.png"));
mBannerInfos.add(new BannerInfo("https://i.loli.net/2018/04/07/5ac8459fc9b6a.png"));
mBannerInfos.add(new BannerInfo("https://i.loli.net/2018/04/06/5ac7339ee876e.jpg"));
mRecyclerViewBanner.setRvBannerData(mBannerInfos);
mRecyclerViewBanner.setOnSwitchRvBannerListener(new OnSwitchRvBannerListener() {
@Override
public void switchBanner(int position, SimpleDraweeView draweeView) {
draweeView.setImageURI(mBannerInfos.get(position).getUrl());
}
});
mRecyclerViewBanner.setListener(new OnRvBannerClickListener() {
@Override
public void OnClick(int position) {
toWebActivity(UrlInfoBean.homeBannerUrls[position]);
}
});
}
};
madapters.add(bannerAdapter);
在這里,我們可以看到買(mǎi)之前我們所說(shuō)的通欄布局,也就是展現(xiàn)一個(gè)視圖的singleLayoutHelper,這里adapter就是我們上面所說(shuō)的自定義的Adapter,因?yàn)槊總€(gè)視圖對(duì)應(yīng)的adapter都是不同的,所以我們需要重寫(xiě)onCreateViewHolder與 onBindViewHolder方法,然后將編寫(xiě)好的輪播圖adapter布局添加至list結(jié)合中,至此,輪播圖的界面編寫(xiě)完成。
下面,我們將考慮第二種布局界面的編寫(xiě):網(wǎng)格布局 看下圖

如圖所示,上部分類(lèi)導(dǎo)航欄就是網(wǎng)格布局。單個(gè)布局很簡(jiǎn)單,就是一張圖片加一個(gè)TextView,
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/White"
android:gravity="center"
android:orientation="vertical"
android:paddingBottom="10dp">
<ImageView
android:id="@+id/iv_home_one_grid_icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginTop="5dp"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/iv_home_one_grid_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/Black" />
</LinearLayout>
挺簡(jiǎn)單的,下面我們將其也添加至RecyclerView里面
//加載主頁(yè)兩行網(wǎng)格布局
GridLayoutHelper gridLayoutHelper = new GridLayoutHelper(4);
GeneralVLayoutAdapter gridAdapter = new GeneralVLayoutAdapter(
getContext(), gridLayoutHelper, 8) {
@NonNull
@Override
public MainViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(getContext()).inflate(
R.layout.home_pager_grid_two_line, viewGroup, false);
return new MainViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MainViewHolder mainViewHolder, int i) {
super.onBindViewHolder(mainViewHolder, i);
ImageView imageView = mainViewHolder.itemView.findViewById(R.id.iv_home_one_grid_icon);
TextView textView = mainViewHolder.itemView.findViewById(R.id.iv_home_one_grid_title);
switch (i) {
case 0:
textView.setText("充值中心");
imageView.setImageResource(R.drawable.icon_voucher_center);
imageView.setOnClickListener(new MyClick(1));
break;
case 1:
textView.setText("手機(jī)通訊");
imageView.setImageResource(R.drawable.icon_phone);
imageView.setOnClickListener(new MyClick(2));
break;
case 2:
textView.setText("電影票");
imageView.setImageResource(R.drawable.icon_movie);
imageView.setOnClickListener(new MyClick(3));
break;
case 3:
textView.setText("全民游戲");
imageView.setImageResource(R.drawable.icon_game);
imageView.setOnClickListener(new MyClick(4));
break;
case 4:
textView.setText("代還信用卡");
imageView.setImageResource(R.drawable.icon_pay_card);
imageView.setOnClickListener(new MyClick(5));
break;
case 5:
textView.setText("現(xiàn)金分期");
imageView.setImageResource(R.drawable.icon_cash_fenqi);
imageView.setOnClickListener(new MyClick(6));
break;
case 6:
textView.setText("辦信用卡");
imageView.setImageResource(R.drawable.icon_ban_card);
imageView.setOnClickListener(new MyClick(7));
break;
case 7:
textView.setText("全部分類(lèi)");
imageView.setImageResource(R.drawable.icon_all_classify);
imageView.setOnClickListener(new MyClick(8));
break;
default:
break;
}
}
};
madapters.add(gridAdapter);
之前已經(jīng)說(shuō)明了,需要重寫(xiě)兩個(gè)方法,就不再贅述了。這樣就將網(wǎng)格布局添加進(jìn)去了。下面是新的布局,直接上XML代碼
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/rl_goods_title_layout"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:background="@color/White">
<TextView
android:id="@+id/tv_home_goods_title_image"
android:layout_width="5dp"
android:layout_height="20dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginStart="10dp"
android:background="@drawable/shape_home_goods_title" />
<TextView
android:id="@+id/tv_home_goods_title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="10dp"
android:layout_toRightOf="@+id/tv_home_goods_title_image"
android:text="活動(dòng)"
android:textColor="@color/Black"
android:textSize="16sp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="10dp"
android:src="@drawable/user_icon_arrow_right" />
</RelativeLayout>
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/iv_home_goods_bigImage"
android:layout_width="match_parent"
android:layout_height="150dp"
android:scaleType="centerCrop" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="120dp"
android:orientation="horizontal">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/iv_home_goods_item_1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="0.1dp"
android:layout_weight="1"
android:scaleType="fitXY" />
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/iv_home_goods_item_2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="0.1dp"
android:layout_weight="1"
android:scaleType="fitXY" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginTop="0.2dp"
android:orientation="horizontal">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/iv_home_goods_item_3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="0.1dp"
android:layout_weight="1"
android:scaleType="fitXY" />
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/iv_home_goods_item_4"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="0.1dp"
android:layout_weight="1"
android:scaleType="fitXY" />
</LinearLayout>
</LinearLayout>
這個(gè)XML文件也很簡(jiǎn)單,首先是Title,然后下面是一張SimpleDraweeView,最后是四張SimpleDraweeView在一起的矩形圖
我們也將其添加至recyclerView中
int count = mHomeSortInfos.size();
GridLayoutHelper sortHelp = new GridLayoutHelper(1);
GeneralVLayoutAdapter sortAdapter = new GeneralVLayoutAdapter(getContext(), sortHelp, count) {
@NonNull
@Override
public MainViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(
getActivity()).inflate(R.layout.home_pager_goods_layout, viewGroup, false);
return new MainViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MainViewHolder mainViewHolder, final int i) {
super.onBindViewHolder(mainViewHolder, i);
HomeSortInfo homeSortInfo = mHomeSortInfos.get(i);
TextView textTitle = mainViewHolder.itemView.findViewById(R.id.tv_home_goods_title_text);
textTitle.setText(homeSortInfo.getTitle());
SimpleDraweeView draweeView = mainViewHolder.itemView.findViewById(R.id.iv_home_goods_bigImage);
draweeView.setImageURI(homeSortInfo.getSortImageUrl());
draweeView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toWebActivity(UrlInfoBean.homeHeaderUrls[i]);
}
});
TextView textLeftImage = mainViewHolder.itemView.findViewById(R.id.tv_home_goods_title_image);
switch (i) {
case 1:
textLeftImage.setBackground(getContext().getResources().getDrawable(R.drawable.shape_home_goods_title1));
break;
case 2:
textLeftImage.setBackground(getContext().getResources().getDrawable(R.drawable.shape_home_goods_title2));
break;
case 3:
textLeftImage.setBackground(getContext().getResources().getDrawable(R.drawable.shape_home_goods_title3));
break;
default:
break;
}
RelativeLayout heardLayout = mainViewHolder.itemView.findViewById(R.id.rl_goods_title_layout);
heardLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(getContext(), ClassifyGoodsActivity.class));
getActivity().overridePendingTransition(R.anim.activity_right_in, 0);
}
});
List<HomeSortItemfo> sortItemfos = homeSortInfo.getmItemfos();
SimpleDraweeView draweeView1Item1 = mainViewHolder.itemView.findViewById(R.id.iv_home_goods_item_1);
SimpleDraweeView draweeView1Item2 = mainViewHolder.itemView.findViewById(R.id.iv_home_goods_item_2);
SimpleDraweeView draweeView1Item3 = mainViewHolder.itemView.findViewById(R.id.iv_home_goods_item_3);
SimpleDraweeView draweeView1Item4 = mainViewHolder.itemView.findViewById(R.id.iv_home_goods_item_4);
draweeView1Item1.setImageURI(sortItemfos.get(0).getGoodsImageUrl());
draweeView1Item2.setImageURI(sortItemfos.get(1).getGoodsImageUrl());
draweeView1Item3.setImageURI(sortItemfos.get(2).getGoodsImageUrl());
draweeView1Item4.setImageURI(sortItemfos.get(3).getGoodsImageUrl());
draweeView1Item1.setOnClickListener(new MyClick("iv_home_goods_item_1"));
draweeView1Item2.setOnClickListener(new MyClick("iv_home_goods_item_2"));
draweeView1Item3.setOnClickListener(new MyClick("iv_home_goods_item_3"));
draweeView1Item4.setOnClickListener(new MyClick("iv_home_goods_item_4"));
}
};
madapters.add(sortAdapter);
mdelegateAdapter.setAdapters(madapters);
}
很簡(jiǎn)單吧,這樣我們的布局界面就完成了,接下來(lái),需要通過(guò)網(wǎng)絡(luò)獲取資源文件
mHomeSortInfos = new ArrayList<>();
mHomeSortItemfos = new ArrayList<>();
final JsonUtil jsonUtil = new JsonUtil();
OkHttpUtil.getInstance().startGet(UrlInfoBean.homeGoodsUrl, new OnNetResultListener() {
@Override
public void OnSuccessListener(String result) {
LogUtil.d("LF1234", "result=" + result);
mHomeSortInfos = jsonUtil.getDataFromJson(result, 0);
LogUtil.d("LF123", "mHomeSortInfos=" + mHomeSortInfos);
Message message = mHandler.obtainMessage(0x01, mHomeSortInfos);
mHandler.sendMessage(message);
}
ok,下面看下效果如何。

嗯,有些缺陷,輪播圖的圖片還沒(méi)有顯示出來(lái),但是大體的功能界面已經(jīng)完成,后期會(huì)改進(jìn),歡迎大家評(píng)論,留言,指導(dǎo),感謝大家的閱讀,謝謝!