商城首页App的编写,首先我们要确定我们需要确定一下大框架:
如图所示,我们可以看到,首先我们需要一个内容根布局,和一个标题栏,之后是RecyclerView,之所以使用RecyclerView,是因为我们通过联网访问需要获取到实时数据。可以根据内容自定义我们需要的View,然后添加到RecyclerView中,有多少就能写多了.不用担心数据会储存不下去。好了,基本框架说完了,让我们来看看如何实现。
首先是关于ToolBar的标题栏和状态栏,因为手机本身显示内容界面有限,所以,为了保证用户能够流畅的去使用我们的app,就需要对标题栏和状态栏进行改动。
一.沉浸式状态栏:
何为沉浸式状态栏,有人可能说,像饿了吗,美团,qq那种状态栏和背景色一致,就是沉浸式状态栏,其实不然,这里参考郭霖大神的博客https://blog.csdn.net/guolin_blog/article/details/51763825
何为沉浸式,就是像游戏,电影,电视剧那样给人带来观赏体验,让人沉浸其中,不受系统UI的影响,比如像下图:
像这样,用户不会被标题栏与状态栏所干扰,称为沉浸式状态栏。
其他的App则是被称作透明状态栏这里我简单介绍下透明状态栏如何实现
首先现将状态栏和标题栏隐藏:
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(option);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
效果如下:
当然,这也不是沉浸式的,只是隐藏了标题栏和状态栏,应该算是全屏模式吧,引导页就可以这么制作。下面我们修改下代码:
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();
透明状态栏只有在5.0的时候才会有效果,所以我们需要对这里进行SDK的判断。接下来我们使用了SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和SYSTEM_UI_FLAG_LAYOUT_STABLE,注意两个Flag必须要结合在一起使用,表示会让应用的主体内容占用系统状态栏的空间,最后再调用Window的setStatusBarColor()方法将状态栏设置成透明色就可以了。效果如下:
好了,完成透明状态栏以后,我们开始编写布局代码:
布局还是很简单的,这里不再赘述了,我们所需要的就是从网络获取到json数据,然后再讲json转化为我们需要的信息展示到用户UI界面上
所以我们需要对RecyclerView进行定制。这里介绍阿里的开源框架VirtualLayout。
VirtualLayout是什么:
VirtualLayout是一个针对RecyclerView的LayoutManager扩展, 主要提供一整套布局方案和布局间的组件复用的问题。
VirtualLayout有什么作用
通过定制化的LayoutManager,接管整个RecyclerView的布局逻辑;LayoutManager管理了一系列LayoutHelper,LayoutHelper负责具体布局逻辑实现的地方;每一个LayoutHelper负责页面某一个范围内的组件布局;不同的LayoutHelper可以做不同的布局逻辑,因此可以在一个RecyclerView页面里提供异构的布局结构,这就能比系统自带的LinearLayoutManager、GridLayoutManager等提供更加丰富的能力。同时支持扩展LayoutHelper来提供更多的布局能力。默认通用布局实现,解耦所有的View和布局之间的关系: Linear, Grid, 吸顶, 浮动, 固定位置等。
① LinearLayoutHelper: 线性布局
②GridLayoutHelper: Grid布局, 支持横向的colspan
③FixLayoutHelper: 固定布局,始终在屏幕固定位置显示
④ScrollFixLayoutHelper: 固定布局,但之后当页面滑动到该图片区域才 显示, 可以用来做返回顶部或其他书签等
⑤FloatLayoutHelper:浮动布局,可以固定显示在屏幕上,但用户可以拖拽其位置
⑥ColumnLayoutHelper: 栏格布局,都布局在一排,可以配置不同列之间的宽度比值
⑦SingleLayoutHelper: 通栏布局,只会显示一个组件View
⑧OnePlusNLayoutHelper: 一拖N布局,可以配置1-5个子元素
⑨StickyLayoutHelper: stikcy布局, 可以配置吸顶或者吸底
⑩StaggeredGridLayoutHelper:瀑布流布局,可配置间隔高度/宽度
上述默认实现里可以大致分为两类:一是非fix类型布局,像线性、Grid、栏格等,它们的特点是布局在整个页面流里,随页面滚动而滚动;另一类就是fix类型的布局,它们的子节点往往不随页面滚动而滚动。
所有除布局外的组件复用,VirtualLayout将用来管理大的模块布局组合,扩展了RecyclerView,使得同一RecyclerView内的组件可以复用,减少View的创建和销毁过程。
VirtualLayout的使用
① 初始化LayoutManger
mlayoutManager = new VirtualLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mlayoutManager);
②设置回收复用池大小,(如果一屏内相同类型的 View 个数比较多,需要设置一个合适的大小,防止来回滚动时重新创建 View):
mviewPool = new RecyclerView.RecycledViewPool();
mRecyclerView.setRecycledViewPool(mviewPool);
mviewPool.setMaxRecycledViews(0, 20);
这里,因为界面样式比较多,所以,我们自定义Adapter去继承DelegateAdapter.adapter,按照需求去编写我们的界面
public class GeneralVLayoutAdapter extends DelegateAdapter.Adapter {
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);
}
}
}
这段代码也很简单我们需要关注一下几个方法
onCreateViewHolder 这个方法需要我们去重写,因为每个布局对应的viewHolder都不一样,所以需要我们在不同的布局里去重写对应的ViewHolder。
onBindViewHolder 这个方法也需要我们去重写,对应着ViewHolder的子项的数据进行赋值,
getItemCount 这个方法更简单了,它相当于告诉了RecyclerView有多少个子项,直接返回数据源的长度。因为RecyclerView已经为我们封装好了ViewHolder,所以我们的MainViewHolder直接继承RecyclerView的ViewHolder,这没啥好说的。
private DelegateAdapter mdelegateAdapter;
private List madapters = new LinkedList<>();
mdelegateAdapter = new DelegateAdapter(mlayoutManager, false);
mRecyclerView.setAdapter(mdelegateAdapter);
这里的代码需要我们综合去看,因为一个布局就对应着一个adapter,所以我们需要一个集合去添加这些adapter.下面会在代码展示,就不详细说明了。
接下来我们要准备工具,将获取到的json数据转化为UI界面,我们需要使用到OkHttp
相信很多人都使用过,因为比google推送的HttpURLConnection连接要快捷简便的多。使用方法如下,首先我们需要添加库依赖,编辑Gradle文件
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
同时也要添加权限编辑manifest文件
这样我们就能访问网络,获取JSON数据了
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());
}
});
}
}
这是一个封装好的工具类,思路也很简单,首先我们需要一个okHttpClient的实例,因为我们需要访问网络,所以我们也需要访问URL地址,如果要访问,自然就需要Request对象,需要注意的是我们的访问请求是GET还是Post,其实GET和Post差不多,稍微复杂点,Post需要构建出一个RequestBody对象,来存储我们需要提交的参数,然后用post的方法将参数提交,接下来,就是和Get一样的操作。,好了,这样我们就成功的发送了请求。如果请求成功,我们会得到数据源,这个时候我们就需要对其解析,传送数据分为XML和JSON两种格式,这里我们主要说下如何对JSON数据的解析,XML暂且不做考虑,主要说一下如何解析JSON数据
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 homeSortInfos = new ArrayList<>();
List homeSortItemfos = new ArrayList<>();
JSONObject mJsonObject;
JSONArray mJsonArray;
try {
if (type == HOME_COODS_INFO) {
//首页的商品信息
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 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 mGridInfos1 = new ArrayList<>();
List 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 mallPagerInfos = new ArrayList<>();
List bannerInfos = new ArrayList<>();
List gridInfos = new ArrayList<>();
List goodsInfos = new ArrayList<>();
List 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 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 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;
}
}
这是我自己的解析工具类使用的是JSONObject解析,很简单,我们首先创建数据模型bean,比如说,姓名,年龄,性别,可能都是在同一段节点的同一数据源中,所以我们用bean的模式,然后创建各属性的get和set方法。再用list集合去添加,然后在遍历集合的形式,将解析的数据展示到我们的界面上。下面贴出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数据源
[{"id":"1","name":"**","imageUrl":"****"},{"id":"2","name":"**","imageUrl":"****"},
{"id":"1","name":"**","imageUrl":"****"},.....
]
这里只是举例,我们通过JSONArray将数据源转化成JSONObject,然后通过JSONObject去获取每一个元素的属性值,这里就不在赘述了。
好了,有了这些工具,下面我们来说一下关于展示界面的UI设计。
一、轮播图
轮播图是一种很常见的动态UI界面,如下图所示:
这是网站上的轮播图,用户对于轮播图的反映褒贬不一,但是不可否认,它能够很直观的给用户以需求,介绍自己产品的特色,如何实现轮播图,将是我们的重点,我会使用RecyclerView去实现轮播效果。
首先,我们需要的是自定义控件属性,对,你没有听错,是自定义控件属性,因为有时候控件属性不能满足我们的需求,所以,我们需要自定义控件属性,首先,我们需要在res文件夹下values文件下新建一个attrs文件夹,用于存放我们自定义控件的属性
下面我们来一个个分析一下
-
这是一个资源文件的索引名,类似Id,在代码编写时候,能够很快确定你的自定义控件属性位置。 -
attr name 是申明你想自定义控件的属性的名称 format 则是设定控件属性类型
color:颜色值。boolean:布尔值。dimension:尺寸值。float:浮点值。integer:整型值。string:字符串。fraction:百分数。enum:枚举值。flag:位或运算。reference:参考某一资源ID。
现在我们新建一个类去继承FragmentLayout;
public class RecyclerViewBanner extends FrameLayout {
private static final int DEFAULT_SELECT_COLOR = 0xffffffff;//选中时的颜色
private static final int DEFAULT_UNSELECTED_COLOR = 0X50ffffff;//未来选中时的颜色
private RecyclerView mRecyclerView;
private RecyclerViewAdapter adapter;
private LinearLayout mLinearLayout;
private OnRvBannerClickListener listener;
private OnSwitchRvBannerListener onSwitchRvBannerListener;
private boolean isPlaying;//是否播放
private int startX, startY, currentIndex;//从X轴开始的位置,从Y轴开始的位置
private int mSize;//大小
private int mSpace;//间距
private int mInterval;//时间间隔
private int margin;//距离外边距的距离
private int gravity;//位置
private boolean isShowIndicator;//是否显示指示器
private boolean isAutoPlaying;//是否自动播放
private boolean isTouched;//是否触摸
private List
我们继承FrameLayout需要重写他的三个构造方法
- 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);
}
一般来说,前两个构造函数就足够,第三个构造函数的第三个参数是用来设置默认的style样式,目前不考虑,传入参数为0即可。我们需要注意的是AttributeSet attrs这个参数,它就是需要我们传入的自定义控件属性的参数。我们通过TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RecyclerViewBanner);获取到当前自定义控件属性集合,然后对应编写默认属性。轮播图肯定是图片加指示器,有多少图片,就应该对应多少个指示器,首先我们先编写RecyclerView容器。
mRecyclerView = new RecyclerView(context);
mLinearLayout = new LinearLayout(context);
new PagerSnapHelper().attachToRecyclerView(mRecyclerView);//辅助Recycler进行滚动对齐
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);
这段代码我们要重点是new PagerSnapHelper().attachToRecyclerView(mRecyclerView);//辅助Recycler进行滚动对齐和对RecyclerView的滚动监听,第一个方法是通过对滑动速度定位图片位置这里推荐大家去看这篇https://www.jianshu.com/p/e54db232df62
第二个方法是防止RecyclerView停止滑动后,图片位置产生偏差,用于修正。接下来就需要编写adapter,这个相信大家已经非常熟练了。
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;
}
}
这段代码也没有啥难点,直接使用了simpleDraweeView去填充RecyclerView布局就行了。
有人问了,如何实现轮播,这个还是很简单的,只需要利用handler定时异步操作就行了,我们还需要注意的一个地方是对于触摸的监听
//手指触摸事件
@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);
}
对应的触摸事件是按下:ACTION_DOWN,滑动:ACTION_MOVE,抬起:ACTION_UP,结束:ACTION_CANCEL,根据这些触摸事件,判定轮播图是否停止或开始自动轮播。
这样,我们的轮播图就编写完成了。我们去Fragment添加一下吧
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);
在这里,我们可以看到买之前我们所说的通栏布局,也就是展现一个视图的singleLayoutHelper,这里adapter就是我们上面所说的自定义的Adapter,因为每个视图对应的adapter都是不同的,所以我们需要重写onCreateViewHolder与 onBindViewHolder方法,然后将编写好的轮播图adapter布局添加至list结合中,至此,轮播图的界面编写完成。
下面,我们将考虑第二种布局界面的编写:网格布局 看下图
如图所示,上部分类导航栏就是网格布局。单个布局很简单,就是一张图片加一个TextView,
挺简单的,下面我们将其也添加至RecyclerView里面
//加载主页两行网格布局
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("手机通讯");
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("现金分期");
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("全部分类");
imageView.setImageResource(R.drawable.icon_all_classify);
imageView.setOnClickListener(new MyClick(8));
break;
default:
break;
}
}
};
madapters.add(gridAdapter);
之前已经说明了,需要重写两个方法,就不再赘述了。这样就将网格布局添加进去了。下面是新的布局,直接上XML代码
这个XML文件也很简单,首先是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 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);
}
很简单吧,这样我们的布局界面就完成了,接下来,需要通过网络获取资源文件
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,下面看下效果如何。
嗯,有些缺陷,轮播图的图片还没有显示出来,但是大体的功能界面已经完成,后期会改进,欢迎大家评论,留言,指导,感谢大家的阅读,谢谢!