文章目录

  • 项目说明
    • 成品展示
    • Activity & Fragment
      • Activity
      • Fragment
  • WelcomeActivity
    • 代码
  • MainActivity
    • 代码
  • LoginActivity
    • 代码
  • RegisterActivity
    • 代码
  • MeFragment
    • 代码
  • IndexFragment
    • 广告轮播
    • 主页的其他组件
      • 一级菜单
      • 二级菜单
  • FindFragment
    • 代码

项目说明

慕淘旅游是对UI常用组件的一次集合使用,其中包括Activity和Fragment之间的传值与调用、不同适配器的设置以及ListView、RecycleView等控件的用法等等

成品展示

Activity & Fragment

Activity的代码量不大,仅用于完成简单的布局和页面跳转功能,主要的布局都在Fragment

Activity

  • 打开软件的欢迎页WelcomeActivity
  • 存储显示底部导航按钮MainActivity
  • 登录页面LoginActivity
  • 注册页面RegisterActivity

Fragment

  • 主页IndexFragment
  • 发现页FindFragment
  • 我的MeFragment

WelcomeActivity

欢迎页就是打开APP见到的第一个页面,布局就是一个背景图片加一个logo

打开APP后将会在欢迎页停留3秒,3秒后自动跳转到主页
这里是通过控制线程的休眠来实现停留3秒后跳转的

代码

 @Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_welcome);//通过线程实现进入欢迎页面后等待3秒自动跳转到主页面new Thread(){@Overridepublic void run() {super.run();try {//休眠3秒sleep(3000);//跳转startActivity(new Intent(WelcomeActivity.this, MainActivity.class));finish();} catch (InterruptedException e) {e.printStackTrace();}}}.start();}

要记得在Manifest文件里面将WelcomeActivity设为初始页面
这里有个小知识点,下面这个图片的标题栏是不是很碍眼

同样在Manifest文件中在每个Activity里面加上下面这段代码修改一下样式就能去掉了

android:theme="@style/Theme.AppCompat.Light.NoActionBar"

MainActivity

每个页面都放到了Fragment里,切换的时候只需要显示不同的Fragment就可以了,所以对于主页的布局只有一个导航栏
打开APP后通过底部三个按钮完成页面切换

这篇布局文件太多太复杂,简单的就不贴了,这三个按钮用的是单选控件RadioButton
相似度比较高的属性可以摆到styles.xml文件里,然后通过style="@style/stylename"实现调用,可以大量减少重复代码

代码

OnCreate方法中

 @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);index = new IndexFragment();find = new FindFragment();me = new MeFragment();//实例化FragmentTransaction,实现Fragment的动态添加FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();transaction.add(R.id.container, index);transaction.commit();}

为底部导航栏按钮添加点击方法myClick

 public void myClick(View v) {//底部导航栏单击事件FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();switch (v.getId()) {case R.id.index:transaction.replace(R.id.container, index);break;case R.id.find:transaction.replace(R.id.container, find);break;case R.id.me:transaction.replace(R.id.container, me);break;}transaction.commit();}

LoginActivity

登录界面比较简单,这里并没有实现登录功能只是做了简单的UI

代码

点击右上角的X结束当前界面

 //点击叉号图标退出当前界面findViewById(R.id.back_close).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {finish();}});

点击登录按钮下放的免费注册文本跳转至注册界面

    //点击立即注册文本跳转至注册界面findViewById(R.id.register_txt).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startActivity(new Intent(LoginActivity.this, RegisterActivity.class));finish();}});

RegisterActivity

注册界面和登录界面差不多,这里也只实现UI,并未实现注册功能

代码

点击右上方直接登录文本跳转回登录界面

    //点击直接登录文本跳转至登录界面findViewById(R.id.login_txt).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startActivity(new Intent(RegisterActivity.this, LoginActivity.class));finish();}});

上面四个Activity都比较简单,重点在下面首页、发现页和我的三个Fragment当中
我单独创建了一个DataUtil类,这个类主要是用来存放各个模块所需的图片、文字,方便后面的修改


MeFragment

我的页面是三个页面里相对较为简单的页面

上方是登录按钮,点击后可以跳转至登录界面,下方是ListView控件可以上下滚动显示

代码

登录跳转和之前的跳转没什么差别,唯一要注意的是在Fragment中的环境上下文不再是xxx.this而是通过getContext()方法获取
之前提到的列表是通过实例化SimpleAdapter适配器来实现的
先在布局文件里面创建一个合适的ListView

    <ListViewandroid:id="@+id/me_listview"android:layout_width="match_parent"android:layout_height="wrap_content"android:divider="@null"/>

这里通过设置最后一行属性,将ListView原本的分界线隐藏
使用SimpleAdapter来完成布局还需要创建一个布局文件,在这个文件里实现单个布局,最后可以根据数据的数量来批量实现,这里的me_menu_listview就是我创建的布局文件
首先初始化数据

    private void initData() {//将数据添加到data中for (int i = 0; i < DataUtil.me_imgs.length; i++) {Map<String, Object> map = new HashMap<>();map.put("img", DataUtil.me_imgs[i]);map.put("txt", DataUtil.me_txts[i]);data.add(map);}}

实例化SimpleAdapter

 String[] from = {"img", "txt"};int[] to = {R.id.image_listview, R.id.text_listview};//在Fragment中Context必须通过getContext()来获取//参数1:环境上下文  参数2:要添加的数据源  参数3:布局文件  参数4和参数5:将数据源对应数据添加到对应的布局中SimpleAdapter adapter = new SimpleAdapter(getContext(), data, R.layout.me_menu_listview, from, to);meListView.setAdapter(adapter);

IndexFragment

主页主要分为两大块,一块是上方的广告轮播,这里需要用到ViewPager2控件来实现,另一块就是广告轮播下方的所有内容

广告轮播

感觉这里用帧布局做总体布局会好一些,上面的扫一扫、消息还有输入框都挺简单的,难点集中在中间的广告轮播功能,这里需要添加ViewPager2控件

    <!--广告轮播--><androidx.viewpager2.widget.ViewPager2android:id="@+id/pagers_top"android:layout_width="match_parent"android:layout_height="match_parent" />

IndexFragment中将广告轮播的实现代码写在setViewPager()
在这个方法里需要实例化一个RecyclerView.Adapter适配器
这里是将广告图片作为视图的背景来进行展示,这样的话只需要创建一个空的线性布局adv_item即可

 //实现图片滑动轮播RecyclerView.Adapter adapter = new RecyclerView.Adapter() {//用于获取盛放图片的控件的ViewHolder对象@NonNull@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {//创建一个空白布局,将轮播的图片设置为布局的背景图片View v = LayoutInflater.from(getContext()).inflate(R.layout.adv_item, parent, false);return new ViewHolder(v);}//为ViewHolder对象中的控件设置显示效果@Overridepublic void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {ViewHolder vh = (ViewHolder) holder;vh.v.setBackgroundResource(DataUtil.adv_imgs[position % 4]);}//设置pager的数量,这里设置为Integer的最大值,这样可以一直往右划,不用到最后又返回第一张开始滚动@Overridepublic int getItemCount() {return Integer.MAX_VALUE;}};pagers_top.setAdapter(adapter);

这里返回的是ViewHolder对象,但本身的ViewHolder需要实现其它方法,并不是我想要的,所以自己写一个ViewHolder类来继承这个RecycleView.Adapter

    class ViewHolder extends RecyclerView.ViewHolder {public View v;public ViewHolder(@NonNull View itemView) {super(itemView);v = itemView;}}

完成上面两步就可以实现手动滑动了
下面实现的是图片和它对应指示器的绑定,实现在滑动图片时,下方的指示器也跟着滚动

    //滑动事件:同步指示器、修改自动轮播位置pagers_top.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {@Overridepublic void onPageSelected(int position) {super.onPageSelected(position);for (int i = 0; i < pointers.getChildCount(); i++) {//先将所有指示器设置为未选中状态ImageView img = (ImageView) pointers.getChildAt(i);img.setImageResource(R.drawable.dot_unselected);}//position位置对应的指示器设置为选中状态((ImageView) pointers.getChildAt(position % 4)).setImageResource(R.drawable.dot_selected);//将index设置为当前位置index = position;}});


到这里就实现了广告手动轮播,最后加入线程,通过线程实现自动轮播,并设置每张图片显示的时间,这里显示间隔设置为3秒

     //通过线程来实现自动轮播new Thread() {@Overridepublic void run() {super.run();while (true) {try {sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}pagers_top.setCurrentItem(index);index++;}}}.start();

主页的其他组件

因为内容较多,所以需要用到ScrollView滚动条,但是这里要注意ScrollView内部只能有一个子控件,所以在这个控件里面的子控件都添加到一个线性布局中
这里的难点在于一级菜单

还有二级菜单,这里二级菜单是可以向右滚动的,有点类似于广告轮播

一级菜单

一级菜单是通过GridView控件和SimpleAdapter适配器来实现
GridView控件

 <!--一级菜单--><GridViewandroid:id="@+id/index_grids"android:layout_width="match_parent"android:layout_height="180dp"android:numColumns="4" />

SimpleAdapter适配器
先初始化数据,这里的图片和文本我都放到了DataUtil类里面

    private List<Map<String, Object>> data = new ArrayList<>();//一级菜单数据for (int i = 0; i < DataUtil.index_menu1_imgs.length; i++) {Map<String, Object> map = new HashMap<>();map.put("img", DataUtil.index_menu1_imgs[i]);map.put("txt", DataUtil.index_menu1_txts[i]);data.add(map);}

然后实例化SimpleAdapter就完成添加了

    //设置一级菜单private void setGridView() {String[] from = {"img", "txt"};int[] to = {R.id.index_image_grid, R.id.index_text_grid};SimpleAdapter adapter = new SimpleAdapter(getContext(), data, R.layout.index_menu_grid, from, to);grids.setAdapter(adapter);}

二级菜单

二级菜单比一级菜单实现起来更复杂一点,它是通过RecycleView控件和RecyclerView.Adapter适配器来实现
先初始化数据,这里用的data跟一级菜单是同一个,因为是键值对数据,所以不用担心混淆

    //设置二级菜单数据private void setMenuDate() {//将数据添加至data中for (int i = 0; i < DataUtil.index_menu2_imgs.length; i++) {Map<String, Object> map = new HashMap<>();map.put("img", DataUtil.index_menu2_imgs[i]);map.put("txt", DataUtil.index_menu2_txts[i]);data.add(map);}}

然后实例化RecycleView.Adapter完成添加

 //二级菜单private void setMenuPager() {// 创建视图管理器,设置为水平布局layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);recyclerView.setLayoutManager(layoutManager);RecyclerView.Adapter adapter = new RecyclerView.Adapter() {@NonNull@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View v = LayoutInflater.from(getContext()).inflate(R.layout.menu_item, parent, false);return new ViewHolder(v);}@Overridepublic void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {ViewHolder vh = (ViewHolder) holder;vh.img1.setImageResource(DataUtil.index_menu2_imgs[0]);vh.txt1.setText(DataUtil.index_menu2_txts[0]);vh.img2.setImageResource(DataUtil.index_menu2_imgs[1]);vh.txt2.setText(DataUtil.index_menu2_txts[1]);vh.img3.setImageResource(DataUtil.index_menu2_imgs[2]);vh.txt3.setText(DataUtil.index_menu2_txts[2]);}@Overridepublic int getItemCount() {return Integer.MAX_VALUE;}};recyclerView.setAdapter(adapter);}

同样这里的ViewHolder需要重写一下,因为做广告轮播的时候重写过一次,这次直接在那上面修改就可以了

    class ViewHolder extends RecyclerView.ViewHolder {public View v;public ImageView img1, img2, img3;public TextView txt1, txt2, txt3;public ViewHolder(@NonNull View itemView) {super(itemView);v = itemView;img1 = itemView.findViewById(R.id.image1_menu2);txt1 = itemView.findViewById(R.id.text1_menu2);img2 = itemView.findViewById(R.id.image2_menu2);txt2 = itemView.findViewById(R.id.text2_menu2);img3 = itemView.findViewById(R.id.image3_menu2);txt3 = itemView.findViewById(R.id.text3_menu2);}}

到这里一级菜单和二级菜单已经完成了,剩下的就是一些比较简单的布局

FindFragment

最后发现页也需要一个ScrollView来完成页面滚动

这里的一级菜单和二级菜单用的和主页的一级菜单一样是通过GridViewSimpleAdapter来实现的,中间的旅行PK模块用帧布局来实现,其它都是一些简单的线性布局和相对布局结合实现的
这里的难点在于热门头条下面的三行咨询的显示

这里是通过ListView控件和BaseAdapter自定义适配器来实现

代码

ListView控件

    <ListViewandroid:id="@+id/find_listview"android:layout_width="match_parent"android:layout_height="330dp"android:divider="@null"/>

实例化BaseAdapter适配器
这里的Find_Hot_Msg是新建的一个类,用来存储文字以及图片数据

    // 热门头条块ListViewprivate void setListView() {// 通过自定义适配器BaseAdapter完成BaseAdapter adapter = new BaseAdapter() {// 获取数量(设置ListView的长度)@Overridepublic int getCount() {return list.size();}// 获取视图(设置ListView每一项的显示效果)@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;//完成对convertView的设置/// 将布局资源转为viewif (convertView == null) {//优化:利用进入RecycleBin中的View,减少view的重复赋值convertView = LayoutInflater.from(getContext()).inflate(R.layout.find_hot_listview, null);holder = new ViewHolder();holder.title = convertView.findViewById(R.id.listview_title);holder.from = convertView.findViewById(R.id.listview_from);holder.eye = convertView.findViewById(R.id.listview_eyenum);holder.like = convertView.findViewById(R.id.listview_likenum);holder.img = convertView.findViewById(R.id.listview_img);convertView.setTag(holder);} else {// 优化:// 通过getTag()取出ViewHolder对象,然后能够直接通过holder.控件的方式在外面直接操作控件// 从而避免了大幅度使用findViewById操作// getTag()操作效率比findViewById要高holder = (ViewHolder) convertView.getTag();}Find_Hot_Msg fhm = list.get(position);//标题titleholder.title.setText(fhm.getTitle());//来源fromholder.from.setText(fhm.getFrom());//阅读数量eyeholder.eye.setText(fhm.getEye());//点赞数量likeholder.like.setText(fhm.getLike());//图片imgholder.img.setImageResource(fhm.getPic());return convertView;}// ==============两个酱油方法@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}};listView.setAdapter(adapter);}

这里同样要重写ViewHolder

 // 优化:// 当view为null时,完成对ViewHolder的实例化工作,并为各个控件属性赋值// 性能的提升是在view不为null时体现的// 当view为null时,完成了ViewHolder及内部控件属性的初始化工作后,调用一句代码:view.setTag(holder);// 当view不为null时,holder = view.getTag();static class ViewHolder {TextView title, from, eye, like;ImageView img;}

但是这里会出现一个问题,就是ListView控件是自带一个滚动条的,它和ScrollView只能同时存在一个,不然会发生冲突导致ListView滚动条失效
这里我暂时想到三个解决的办法:

  1. 将每条内容分开放到单独的LinearLayout中,直接作为线性布局来摆放
  2. 如果数据量不大的话,比如像这个发现页只有三行,可以直接将这个ListView控件的高度写死,我就是用的这种方法
  3. 写一个新的MyListView类来继承ListView这个类,重写里面的方法,修改当ListView滑动时ScrollView的拦截事件

下面是实现第三个解决办法的代码:

public class MyListView extends ListView {//重写构造public MyListView(Context context) {this(context, null);}public MyListView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return super.onInterceptTouchEvent(ev);}//为listview/Y,设置初始值,默认为0.0(ListView条目一位置)private float mLastY;@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {//重点在这里int action = ev.getAction();switch (action) {case MotionEvent.ACTION_DOWN:super.onInterceptTouchEvent(ev);//不允许上层viewGroup拦截事件.getParent().requestDisallowInterceptTouchEvent(true);break;case MotionEvent.ACTION_MOVE://满足listView滑动到顶部,如果继续下滑,那就让scrollView拦截事件if (getFirstVisiblePosition() == 0 && (ev.getY() - mLastY) > 0) {//允许上层viewGroup拦截事件getParent().requestDisallowInterceptTouchEvent(false);}//满足listView滑动到底部,如果继续上滑,那就让scrollView拦截事件else if (getLastVisiblePosition() == getCount() - 1 && (ev.getY() - mLastY) < 0) {//允许上层viewGroup拦截事件getParent().requestDisallowInterceptTouchEvent(false);} else {//不允许上层viewGroup拦截事件getParent().requestDisallowInterceptTouchEvent(true);}break;case MotionEvent.ACTION_UP://不允许上层viewGroup拦截事件getParent().requestDisallowInterceptTouchEvent(true);break;}mLastY = ev.getY();return super.dispatchTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent ev) {return super.onTouchEvent(ev);}
}

完成这个类之后在布局文件中将ListView控件替换成MyListView再设置一些参数就可以了


源码链接: https://download.csdn.net/download/lckgr/12616257

Android-UI 慕淘旅游相关推荐

  1. Android Virtualview:淘宝、天猫又开源了一个动态化、高性能的UI框架

    转载 Carson_Ho Android Virtualview:淘宝.天猫又开源了一个动态化.高性能的UI框架 前言 目录 1. 为什么要向 Tangram模型 加入 VirtualView 2. ...

  2. Android Virtualview:淘宝、天猫 又一个动态化、高性能的UI框架力作

    Android Virtualview:淘宝.天猫 又一个动态化.高性能的UI框架力作 前言 淘宝.天猫一直致力于解决 页面动态化的问题 在2017年的4月发布了v1.0解决方案:Tangram模型 ...

  3. Android 开发 -- 开发第一个安卓程序、Android UI开发(布局的创建:相对布局和线性布局、控件单位:px pt dp sp、常用控件 、常见对话框、ListView)

    文章目录 1. 开发第一个Hello World程序 1.1 开发程序 1.2 认识程序中的文件 1.3 Android程序结构 1.4 安卓程序打包 2. Android UI开发 2.1 布局的创 ...

  4. GitHub 优秀的 Android 开源项目 淘宝技术牛p博客整理开发中最常用的GitHub上 优秀的 Android 开源项目整理(精品)...

    原文地址为http://www.trinea.cn/android/android-open-source-projects-view/,作者Trinea 主要介绍那些不错个性化的View,包括Lis ...

  5. 各种Android UI开源框架 开源库

    各种Android UI开源框架 开源库 转 https://blog.csdn.net/zhangdi_gdk2016/article/details/84643668 自己总结的Android开源 ...

  6. Android Studio 集成淘宝三方登录

    满足下面条件1-- classpath 'com.android.tools.build:gradle:2.3.2' 版本不要高于3.02-- 通过阿里百川开通应用3-- 项目开通过电商能力4-- 项 ...

  7. Android UI 阿里VLayout使用

    Android UI 阿里VLayout使用 开源地址(2017.03开源):https://github.com/alibaba/vlayout/ VirtualLayout是一个针对Recycle ...

  8. Android UI开发第二十五篇——分享一篇自定义的 Action Bar

    Action Bar是android3.0以后才引入的,主要是替代3.0以前的menu和tittle bar.在3.0之前是不能使用Action Bar功能的.这里引入了自定义的Action Bar, ...

  9. Android UI滑动加载源码

    2019独角兽企业重金招聘Python工程师标准>>> android UI 往右滑动,滑动到最后一页就自动加载数据并显示 如图: Java代码 package cn.anycall ...

最新文章

  1. 大学生目前普遍存在的问题,看你中招了没?
  2. element怎么设置复选框属性_【JavaScript 教程】DOM——属性的操作
  3. OS X 要改名为 MacOS 是因为 iOS 10 要来了?
  4. for循环 消耗时间计算
  5. js 函数定义的方式
  6. PHP解决shell_exec has been disabled for security reasons
  7. 前端JavaScript基础知识点
  8. 华中科技大学 计算机组成原理 上机实验1 2018
  9. 使用Ext.grid.Panel显示远程数据
  10. 用python实现求最大公约数_python实现求最大公约数与最小公倍数
  11. java 面向对象之内存管理
  12. 办公软件excel表格_EXCEL表格模板下载推荐?办公资源网优质海量素材资源免费下载...
  13. 自定义B站视频播放速度
  14. 大数据工具主要分为哪几类,每类中具体有哪些工具?
  15. 用算符优先法对算术表达式求值(六)
  16. Git如何获取帮助(命令解释/命令详情)?
  17. DRM框架(vkms)分析(9)----drm驱动创建fbdevice分析(以rockchip_drm_drv为例)
  18. 物联网技术与应用期末大作业
  19. 顺序表(一篇带你掌握顺序表)
  20. 《人机交互:软件工程视角》期末复习提纲

热门文章

  1. 删除数组中的重复数内容
  2. Nick认为的SDN三阶段
  3. Wonderware INSQL historian SDK开发
  4. 听闻electron 打包可以让我费很长时间
  5. 使用easyExcel导出excel文件
  6. termius破解使用sftp
  7. Tableau实战练习1
  8. Liunx环境搭建5--Docker环境搭建 tomcat+Jenkins+Python+Allure 测试环境。方法二
  9. AI人工智能标记数据的技术:类型、方法、质量控制、应用
  10. Springboot中手动new的对象无法注入交给Spring容器管理的原因及解决办法