Android项目实战:商城APPday02主页
不否认努力,继续加油!
学习整理重点、盲区,笔记如下:干干巴巴,麻麻赖赖,一点都不圆润……
源码 已上传至集哈: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.