Android项目实战:商城APPday02主页
- day02
- 内容
- 1. 主页面结构分析
- 2. 请求主页数据和解决数据
- 3. 主页面适配器
- 4. 设置横幅广播的适配器
- 5. 频道适配器
- 6. 活动适配器
- 7. 秒杀适配器
- 8. 推荐适配器
- 9. 热卖适配器
- 10. 设置监听 RecyclerView 的位置
- 11. 商品信息列表类 GoodsInfoActivity
- 盲区
- 其他实战
- 商城
- 新闻
不否认努力,继续加油!
学习整理重点、盲区,笔记如下:干干巴巴,麻麻赖赖,一点都不圆润……
源码 已上传至集哈:ShoppingMall.
https://github.com/SmileAlfred/ShoppingMall
day02
内容
1. 主页面结构分析
- 布局分析;
除去 RadioButton 外,上半啦整体是一个 相对布局,
title + RecyclerView + FloatButton; - 其中 title 用线性布局;利用 DrawableLeft 和 DrawableTop 设置两文本;
- RecyclerView 需要对不同显示,设置不同的 6 种适配器;
- 初始化布局和设置监听
需要使用 view 去实例化控件,不可以直接 findViewById();
@Override
public View initView() {
View view = View.inflate(mContext, R.layout.fragment_home, null);
rvHome = (RecyclerView) view.findViewById(R.id.rv_home);
ib_top = (ImageView) view.findViewById(R.id.ib_top);
tv_search_home = (TextView) view.findViewById(R.id.tv_search_home);
tv_message_home = (TextView) view.findViewById(R.id.tv_message_home);
//设置点击事件
initListener();
return view;
}
2. 请求主页数据和解决数据
- 使用 OkHttpUtil s 请求网络
添加依赖:implementation 'com.github.xxl6097:okhttputils:2.4.1'
添加联网权限:
private void getDataFromNet() {
String url = Constants.HOME_URL;
OkHttpUtils.get()
.url(url)
.build()
.execute(new StringCallback() {
/**
* 当请求失败的时候回调
*/
@Override
public void onError(Call call, Exception e) {
Log.e(TAG, "首页请求失败==" + e.getMessage());
}
/**
* 当联网成功的时候回调
*/
@Override
public void onResponse(Call call, String s) {
Log.e(TAG, "首页请求成功==" + s);
//解析数据
processData(s);
}
});
}
- 配置联网路径
//这里创建了一个常量类,其中的常量用静态字符串表示;
public class Constants {
public static String BASE_URL = "http://192.168.0.9:8080/atguigu";
/**
* 主页面的路径
*/
public static String HOME_URL = BASE_URL+"/json/HOME_URL.json";
/**
* 图片的基本路径
*/
public static String BASE_URL_IMAGE = BASE_URL+"/img";
}
- 使用 fastjson(By Alibaba) 解析数据
添加依赖:implementation 'com.alibaba:fastjson:1.2.68'
private void processData(String json) {
if (!TextUtils.isEmpty(json)) {
ResultBeanData resultBeanData = JSON.parseObject(json, ResultBeanData.class);
resultBean = resultBeanData.getResult();
Log.e(TAG, "解析成功==" + resultBean.getHot_info().get(0).getName());
}
}
- 生成 JeanBean
使用插件 GsonFromat 生成 Bean 对象;
3. 主页面适配器
- 选择 RecyclerView,因为其中可以使用不同种类的多种 adapter;首页有六种不同的效果,分别是如下,广告条,分类,ViewPager,秒杀栏,三栏的 RecyclerView,两栏的 RecyclerView;
- 六种类型的 ViewHolder ,设置类型,
/**
* 广告条幅类型、频道类型、活动类型、秒杀类型、推荐类型、热卖;
*/
public static final int BANNER = 0;
public static final int CHANNEL = 1;
public static final int ACT = 2;
public static final int SECKILL = 3;
public static final int RECOMMEND = 4;
public static final int HOT = 5;
/**
* 当前类型
*/
private int currentType = BANNER;
- 适配器代码
/**
* 数据对象
*/
private ResultBean resultBean;
private Context mContext;
private LayoutInflater mLayoutInflater;
@Override
public int getItemCount() {
//以后做完后改成6,现在只实现横幅广告,暂时写1
return 1;
}
@Override
public int getItemViewType(int position) {
switch (position) {
case BANNER:
currentType = BANNER;
break;
case CHANNEL:
currentType = CHANNEL;
break;
case ACT:
currentType = ACT;
break;
case SECKILL:
currentType = SECKILL;
break;
case RECOMMEND:
currentType = RECOMMEND;
break;
case HOT:
currentType = HOT;
break;
}
return currentType;
}
public HomeRecycleAdapter(Context mContext, ResultBean resultBean) {
this.mContext = mContext;
this.resultBean = resultBean;
mLayoutInflater = LayoutInflater.from(mContext);
}
4. 设置横幅广播的适配器
- 关联使用 Banner 库;
实现效果:切换页面像手风琴一样的推动; - 设置适配器继承自 RecyclerView.Adapter;
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == BANNER) {
return new BannerViewHolder(mLayoutInflater.inflate(R.layout.banner_viewpager, null), mContext, resultBean);
}
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == BANNER) {
BannerViewHolder bannerViewHolder = (BannerViewHolder) holder;
//设置数据Banner的数据
bannerViewHolder.setData(resultBean.getBanner_info());
}
}
/**
* 设置适配器
*/
class BannerViewHolder extends RecyclerView.ViewHolder {
public Banner banner;
public Context mContext;
public ResultBean resultBean;
public BannerViewHolder(View itemView, Context mContext, ResultBean resultBean) {
super(itemView);
banner = (Banner) itemView.findViewById(R.id.banner);
this.mContext = mContext;
this.resultBean = resultBean;
}
public void setData(final List<ResultBean.BannerInfoBean> banner_info) {
setBannerData(banner_info);
}
}
- 使用 Banner 库
private void setBannerData(final List<ResultBean.BannerInfoBean> banner_info) {
//设置循环指标点
banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR);
//如果想用自己项目的图片加载,就自定义图片加载框架
List<String> imageUris = new ArrayList<>();
for (int i = 0; i < resultBean.getBanner_info().size(); i++) {
imageUris.add(resultBean.getBanner_info().get(i).getImage());
}
// 设置类似手风琴动画
banner.setBannerAnimation(Transformer.Accordion);
//设置加载图片
banner.setImages(imageUris, new OnLoadImageListener() {
@Override
public void OnLoadImage(ImageView view, Object url) {
Glide.with(mContext).load(Constants.Base_URl_IMAGE + url).into(view);
}
});
//设置点击事件
banner.setOnBannerClickListener(new OnBannerClickListener() {
@Override
public void OnBannerClick(int position) {
Toast.makeText(mContext, "position==" + position, Toast.LENGTH_SHORT).show();
}
});
}
- 设置布局管理者
此时运行是不会有效果的,因为没有设置布局管理者;GridLayoutManager manager = new GridLayoutManager(mContext, 1);
首页设置完适配器后,要及时添加如上布局管理者,这里选择GridLayout 而不是选择 RelativeLayout 和 LinearLayout;
5. 频道适配器
- 设置适配器继承自 BaseAdapter;
实现效果: - 和 Banner 适配器类似
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == BANNER) {
return new BannerViewHolder(mLayoutInflater.inflate(R.layout.banner_viewpager, null), mContext, resultBean);
}else if (viewType == CHANNEL) {
return new ChannelViewHolder(mLayoutInflater.inflate(R.layout.channel_item, null), mContext);
}
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == BANNER) {
BannerViewHolder bannerViewHolder = (BannerViewHolder) holder;
//设置数据Banner的数据
bannerViewHolder.setData(resultBean.getBanner_info());
}else if (getItemViewType(position) == CHANNEL) {
ChannelViewHolder channelViewHolder = (ChannelViewHolder) holder;
channelViewHolder.setData(resultBean.getChannel_info());
}
/**
* 设置适配器
*/
class ChannelViewHolder extends RecyclerView.ViewHolder {
public GridView gvChannel;
public Context mContext;
public ChannelViewHolder(View itemView, Context mContext) {
super(itemView);
gvChannel = (GridView) itemView.findViewById(R.id.gv_channel);
this.mContext = mContext;
}
public void setData(final List<ResultBean.ChannelInfoBean> channel_info) {
gvChannel.setAdapter(new ChannelAdapter(mContext, channel_info));
//点击事件 gvChannel.setOnItemClickListener(new
AdapterView.OnItemClickListener() {
@Override public void onItemClick (AdapterView < ? > parent, View view,int position,
long id){
if (position <= 8) {
Toast.makeText(mContext, "position==" + position, Toast.LENGTH_SHORT).show();
}
}
});
}
}
- 不同的是,这里不用设置布局管理者也可以显示,原因应该是继承的 adapter 的原因;奇怪不……不理解……
- 频道适配器 ChannelAdapter
public class ChannelAdapter extends BaseAdapter {
private Context mContext;
private List<ResultBean.ChannelInfoBean> channel_info;
public ChannelAdapter(Context mContext, List<ResultBean.ChannelInfoBean> channel_info) {
this.mContext = mContext;
this.channel_info = channel_info;
}
@Override
public int getCount() {
return channel_info == null ? 0 : channel_info.size();
}
@Override
public Object getItem(int position) {
return channel_info.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holer;
if (convertView == null) {
convertView = View.inflate(mContext, R.layout.item_channel, null);
holer = new ViewHolder(convertView);
convertView.setTag(holer);
} else {
holer = (ViewHolder) convertView.getTag();
}
ResultBean.ChannelInfoBean channelInfoBean = channel_info.get(position);
holer.tvChannel.setText(channelInfoBean.getChannel_name());
Glide.with(mContext).load(Constants.Base_URl_IMAGE + channelInfoBean.getImage()).into(holer.ivChannel);
return convertView;
}
static class ViewHolder {
@Bind(R.id.iv_channel)
ImageView ivChannel;
@Bind(R.id.tv_channel)
TextView tvChannel;
ViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
}
- 写布局文件;
6. 活动适配器
- 写布局,实现如下广告条效果:
- 设置适配器;这里用到的是 PagerAdapter;
class ActViewHolder extends RecyclerView.ViewHolder {
public ViewPager actViewPager;
public Context mContext;
public ActViewHolder(View itemView, Context mContext) {
super(itemView);
actViewPager = (ViewPager) itemView.findViewById(R.id.act_viewpager);
this.mContext = mContext;
}
public void setData(final List<ResultBean.ActInfoBean> data) {
//设置每个页面的间距
actViewPager.setPageMargin(20); //>=3
actViewPager.setOffscreenPageLimit(3);
//第三方库实现页面切换的不同动画:implementation 'com.zhy:magic-viewpager:1.0.1'
actViewPager.setPageTransformer(true, new AlphaPageTransformer(new ScaleInTransformer()));
actViewPager.setAdapter(new PagerAdapter() {
@Override
public int getCount() {
return data.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView view = new ImageView(mContext);
view.setScaleType(ImageView.ScaleType.FIT_XY);
//绑定数据
Glide.with(mContext).load(Constants.Base_URl_IMAGE + data.get(position).getIcon_url()).into(view)
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
});
//点击事件
actViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset,int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
Toast.makeText(mContext, "position:" + position, Toast.LENGTH_SHORT).show();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
}
7. 秒杀适配器
- 写布局,实现如下效果:上边时横向的linear layout;下面是横滑的 RecyclerView;
- 设置 SeckillViewHolder ;
class SeckillViewHolder extends RecyclerView.ViewHolder {
//与上述类似,省略相同方法;……
public void setData(final ResultBeanData.ResultBean.SeckillInfoBean seckill_info) {
//1.设置数据:文本和RecyclerView的数据
adapter = new SeckillRecyclerViewAdapter(mContext, seckill_info.getList());
rv_seckill.setAdapter(adapter);
//2.设置布局管理器
rv_seckill.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false));
//3.设置item的点击事件
adapter.setOnSeckillRecyclerView(new SeckillRecyclerViewAdapter.OnSeckillRecyclerView() {
@Override
public void onItemClick(int position) {
Toast.makeText(mContext, "秒杀" + position, Toast.LENGTH_SHORT).show();
}
});
//4.秒杀倒计时 -毫秒
dt = Integer.valueOf(seckill_info.getEnd_time()) - Integer.valueOf(seckill_info.getStart_time());
handler.sendEmptyMessageDelayed(0, 1000);
}
//5.设置倒计时
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
dt = dt - 1000;
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
String time = formatter.format(new Date(dt));
tv_time_seckill.setText(time);
handler.removeMessages(0);
handler.sendEmptyMessageDelayed(0, 1000);
if (dt <= 0) {
//把消息移除
handler.removeCallbacksAndMessages(null);
}
}
};
}
- 设置横滑 RecyclerView 的适配器和监听器;
public class SeckillRecyclerViewAdapter extends RecyclerView.Adapter<SeckillRecyclerViewAdapter.ViewHodler> {
private final List<ResultBeanData.ResultBean.SeckillInfoBean.ListBean> list;
private final Context mContext;
public SeckillRecyclerViewAdapter(Context mContext, List<ResultBeanData.ResultBean.SeckillInfoBean.ListBean> list) {
this.list = list;
this.mContext = mContext;
}
@Override
public ViewHodler onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = View.inflate(mContext, R.layout.item_seckill, null);
return new ViewHodler(itemView);
}
@Override
public void onBindViewHolder(ViewHodler holder, int position) {
//1.根据位置得到对应的数据
ResultBeanData.ResultBean.SeckillInfoBean.ListBean listBean = list.get(position);
//2.绑定数据
Glide.with(mContext).load(Constants.BASE_URL_IMAGE + listBean.getFigure()).into(holder.iv_figure);
holder.tv_cover_price.setText(listBean.getCover_price());
holder.tv_origin_price.setText(listBean.getOrigin_price());
}
@Override
public int getItemCount() {
return list.size();
}
class ViewHodler extends RecyclerView.ViewHolder {
private ImageView iv_figure;
private TextView tv_cover_price;
private TextView tv_origin_price;
public ViewHodler(View itemView) {
super(itemView);
iv_figure = (ImageView) itemView.findViewById(R.id.iv_figure);
tv_cover_price = (TextView) itemView.findViewById(R.id.tv_cover_price);
tv_origin_price = (TextView) itemView.findViewById(R.id.tv_origin_price);
tv_origin_price.getPaint().setFlags(Paint. STRIKE_THRU_TEXT_FLAG );//给原价 TextView 添加横滑线
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, "秒杀="+getLayoutPosition(), Toast.LENGTH_SHORT).show();
if (onSeckillRecyclerView != null) {
onSeckillRecyclerView.onItemClick(getLayoutPosition());
}
}
});
}
}
/**
* 监听器
*/
public interface OnSeckillRecyclerView {
//当某条被点击的时候回调
public void onItemClick(int position);
}
private OnSeckillRecyclerView onSeckillRecyclerView;
//设置item的监听
public void setOnSeckillRecyclerView(OnSeckillRecyclerView onSeckillRecyclerView) {
this.onSeckillRecyclerView = onSeckillRecyclerView;
}
}
8. 推荐适配器
- 写布局,实现如下效果:上边时横向的 LinearLayout;下面是三列形式的 GridView;
<GridView
android:id="@+id/gv_recommend"
android:layout_width="match_parent"
android:layout_height="380dp"
android:numColumns="3"/>
- 设置 RecommendViewHolder;
- 设置适配器和监听器;
9. 热卖适配器
- 写布局,实现如下效果:上边时横向的 LinearLayout;下面是两列形式的 GridView;
- 设置 RecommendViewHolder;
- 设置适配器和监听器;
10. 设置监听 RecyclerView 的位置
- 隐藏和显示回到顶部按钮
实现目的:当页面滑动不在首页时,右下角的 float button 显示,点击后回到顶部,而后隐藏;
//HomeFragment.java 中 processData();
adapter = new HomeFragmentAdapter(mContext, resultBean);
rvHome.setAdapter(adapter);
GridLayoutManager manager = new GridLayoutManager(mContext, 1);
//设置跨度大小监听
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (position <= 3) {
ib_top.setVisibility(View.GONE);
} else {
ib_top.setVisibility(View.VISIBLE);
}
//只能返回1
return 1;
}
});
//设置布局管理者
rvHome.setLayoutManager(manager);
- 实现点击回到顶部的监听方法;
ib_top.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
rvHome.scrollToPosition(0);
}
11. 商品信息列表类 GoodsInfoActivity
- 商品信息列表类页面实现分析
布局分成三部分:
1:标题栏
2:分割线
3:帧布居
a: 线性布局;里面用 ScrollViewContainer 嵌套两个 ScrollView
b: 线性布局;客服联系,收藏,购物车等
c: 更多;分享, 搜索,首页等 - 写布局 activity_goods_info.xml;
- 布局的实例化和设置点击事件;
- 商品详情页面的数据传递和接收;
传递对象时,要将其序列化;
//传递对象
Intent intent = new Intent(mContext, GoodsInfoActivity.class);
intent.putExtra(GOODS_BEAN,goodsBean);
mContext.startActivity(intent);
//取出intent
Intent intent = getIntent();
goods_bean = (GoodsBean) intent.getSerializableExtra("goods_bean");
- 解析数据并设置商品详情页面数据
- 使用 WebView 加载设置 商品详情 数据
盲区
- 声明:本博客根据尚硅谷项目实战: 硅谷商城.学习整理;
- 对于 okhttputils 一些封装工具,用的不熟悉,尤其是在Json数据解析时间,接下来会深入学习,并会同步更新详细笔记;
- 在设置 RadioButton 的监听器时报错,内容显示 Butterknife 和监听冲突,最后还是老老实实 findViewById(),解了,不过应该对 Butterknife 再研究一下;
- 设置适配器的时候,出现了 BaseAdapter 、 RecyclerView.Adapter 和 PagerAdapter;使用 RecyclerView.Adapter 时要写布局管理者;对于这些适配器还不了解;
- 对于加载商品详情页面时,对于其 json 数据的解析还是存在瑕疵;
- 加油!奥里给!
其他实战
商城
- day01
第一节学习笔记:链接: 商城APP01—框架搭建. - day02
第二节学习笔记:链接: 商城APP02—主页实现. - day03
第三节学习笔记:链接: 商城APP03—购物车实现.
新闻
Android项目实战——新闻APP 学习笔记:链接: 新闻APP.