【0093】【项目实战】-【谷歌电子市场】-【05】【应用详情页-截图模块开发】【应用详情页-描述模块开发】【自定义排行控件的实现】【Actionbar的使用】
1.应用详情页-截图模块开发
1.1 效果及思路
【思路】可以左右滑动,但是不会根据滑动的位置自动的更改位置;
使用水平的srollerView,然后将图片添加进去即可;
1.
1.2 截图的布局
【布局】直接使用HorizontalScrollView容器布局,直接填充图片即可;

1.3.初始化布局
【初始化布局】需要使用一个Holder初始化布局;
【布局文件】准备5个ImageView,虽然有的页面达不到5个,但是也应该准备好;

【源码】/GooglePlay74/res/layout/layout_detail_picinfo.xml 只是五张图片;
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" 5 android:paddingTop="3dp" 6 android:paddingBottom="3dp" 7 android:orientation="horizontal" > 8 9 <ImageView 10 android:id="@+id/iv_pic1" 11 android:layout_width="90dp" 12 android:layout_height="150dp" 13 android:layout_marginLeft="3dp" /> 14 15 <ImageView 16 android:id="@+id/iv_pic2" 17 android:layout_width="90dp" 18 android:layout_height="150dp" 19 android:layout_marginLeft="3dp" /> 20 21 <ImageView 22 android:id="@+id/iv_pic3" 23 android:layout_width="90dp" 24 android:layout_height="150dp" 25 android:layout_marginLeft="3dp" /> 26 27 <ImageView 28 android:id="@+id/iv_pic4" 29 android:layout_width="90dp" 30 android:layout_height="150dp" 31 android:layout_marginLeft="3dp" /> 32 33 <ImageView 34 android:id="@+id/iv_pic5" 35 android:layout_width="90dp" 36 android:layout_height="150dp" 37 android:layout_marginLeft="3dp" /> 38 39 </LinearLayout>
【注意】正确的写法是在initView中写;因为继承在父类中,首先会调用父类的构造方法,然后调用initView();写在外面会导致错误,编译挂掉;

【源码】/GooglePlay74/src/com/itheima/googleplay74/ui/holder/DetailPicsHolder.java
1 package com.itheima.googleplay74.ui.holder; 2 3 import java.util.ArrayList; 4 5 import android.content.Intent; 6 import android.view.View; 7 import android.view.View.OnClickListener; 8 import android.widget.ImageView; 9 10 import com.itheima.googleplay74.R; 11 import com.itheima.googleplay74.domain.AppInfo; 12 import com.itheima.googleplay74.http.HttpHelper; 13 import com.itheima.googleplay74.utils.BitmapHelper; 14 import com.itheima.googleplay74.utils.UIUtils; 15 import com.lidroid.xutils.BitmapUtils; 16 17 /** 18 * 首页详情页-截图 19 * 20 * @author Kevin 21 * @date 2015-11-3 22 */ 23 public class DetailPicsHolder extends BaseHolder<AppInfo> { 24 25 private ImageView[] ivPics; 26 private BitmapUtils mBitmapUtils; 27 28 @Override 29 public View initView() { 30 View view = UIUtils.inflate(R.layout.layout_detail_picinfo); 31 32 ivPics = new ImageView[5]; 33 ivPics[0] = (ImageView) view.findViewById(R.id.iv_pic1); 34 ivPics[1] = (ImageView) view.findViewById(R.id.iv_pic2); 35 ivPics[2] = (ImageView) view.findViewById(R.id.iv_pic3); 36 ivPics[3] = (ImageView) view.findViewById(R.id.iv_pic4); 37 ivPics[4] = (ImageView) view.findViewById(R.id.iv_pic5); 38 39 mBitmapUtils = BitmapHelper.getBitmapUtils(); 40 41 return view; 42 } 43 44 @Override 45 public void refreshView(AppInfo data) { 46 final ArrayList<String> screen = data.screen; 47 48 for (int i = 0; i < 5; i++) { 49 if (i < screen.size()) { 50 mBitmapUtils.display(ivPics[i], HttpHelper.URL + "image?name=" 51 + screen.get(i)); 52 } else { 53 ivPics[i].setVisibility(View.GONE); 54 } 55 } 56 57 } 58 59 }
【应用详情中调用此Holder】填充不同的应用详情的界面的数据;

【BUG】签名一致,先删除安装的应用程序;




【中间的图片的点击查看大图】
【思路】为图片增加点击事件的监听,然后跳转到另外一个Activity,在Activity中使用类似于首页新闻头条的滑动效果,但是需要在跳转的时候传递数据(要加载图片的集合screen和该位置的图片(i));
此处没有实现;



2.应用详情页-描述模块开发
2.1 效果及思路
【思路】就是TextView的组合;

2.2【添加框架中的帧布局】

【初始化布局的数据】

2.3【Hodler的添加】

[描述的布局源码]
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 <TextView 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:text="应用介绍" 11 android:textColor="#9e9e9e" 12 android:textSize="16sp" /> 13 14 <TextView 15 android:id="@+id/tv_detail_des" 16 android:layout_width="match_parent" 17 android:layout_height="wrap_content" 18 android:textColor="#9e9e9e" 19 android:textSize="14sp" /> 20 21 <RelativeLayout 22 android:id="@+id/rl_detail_toggle" 23 android:layout_width="match_parent" 24 android:layout_height="wrap_content" > 25 26 <TextView 27 android:id="@+id/tv_detail_author" 28 android:layout_width="wrap_content" 29 android:layout_height="wrap_content" 30 android:layout_centerVertical="true" 31 android:textColor="#000" 32 android:textSize="14sp" /> 33 34 <ImageView 35 android:id="@+id/iv_arrow" 36 android:layout_width="wrap_content" 37 android:layout_height="wrap_content" 38 android:layout_alignParentRight="true" 39 android:layout_centerVertical="true" 40 android:src="@drawable/arrow_down" /> 41 </RelativeLayout> 42 43 </LinearLayout>
[现阶段的部分源码]
1 package com.itheima.googleplay74.ui.holder; 2 3 import android.util.TypedValue; 4 import android.view.View; 5 import android.view.View.MeasureSpec; 6 import android.view.View.OnClickListener; 7 import android.view.ViewParent; 8 import android.widget.ImageView; 9 import android.widget.LinearLayout; 10 import android.widget.RelativeLayout; 11 import android.widget.ScrollView; 12 import android.widget.TextView; 13 14 import com.itheima.googleplay74.R; 15 import com.itheima.googleplay74.domain.AppInfo; 16 import com.itheima.googleplay74.utils.UIUtils; 17 import com.nineoldandroids.animation.Animator; 18 import com.nineoldandroids.animation.Animator.AnimatorListener; 19 import com.nineoldandroids.animation.ValueAnimator; 20 import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener; 21 22 /** 23 * 详情页-应用描述 24 * 25 * @author Kevin 26 * @date 2015-11-3 27 */ 28 public class DetailDesHolder extends BaseHolder<AppInfo> { 29 30 private TextView tvDes; 31 private TextView tvAuthor; 32 private ImageView ivArrow; 33 private RelativeLayout rlToggle; 34 35 @Override 36 public View initView() { 37 View view = UIUtils.inflate(R.layout.layout_detail_desinfo); 38 39 tvDes = (TextView) view.findViewById(R.id.tv_detail_des); 40 tvAuthor = (TextView) view.findViewById(R.id.tv_detail_author); 41 42 return view; 43 } 44 45 @Override 46 public void refreshView(AppInfo data) { 47 tvDes.setText(data.des); 48 tvAuthor.setText(data.author); 49 50 }); 51 }
【初始化描述的模块的完善】设置数据;

【测试】无法滑动,因为现在是线性布局,应该添加scrllView布局进行包括,就可以滑动了;

2.4【布局中添加scrollView】

【测试2】并非要完成显示所有的文字内容,而是只展示7行的内容;通过点击箭头进行切换完整内容的显示;

【效果】并非要完成显示所有的文字内容,而是只展示7行的内容;通过点击箭头进行切换完整内容的显示;

2.5 文件的部分显示
【思路】模拟七行的TextView的高度,然后将文字的高度进行测量然后设置。


【设置默认的TextView的高度】

【测试3】展示七行的效果

【增加文字的边距】


2.5 增加点击文本显示展开和收起的效果


【注意】要判断描述信息大于7行的时候才启动点击效果

【测试4】达到点击展开的效果,但是还需要点击之后文字自动全部显示;

2.6 点击之后文字自动全部显示
【思路】在接收点击事件之后scrollView自动展开内容;
【scrollView的查找】scrollView是在整个大布局中,而在详情描述并没有该scrollView;此控件的查找有两种思路;
【思路1】通过构造方法传递该参数;
【思路2】详情描述的控件的祖先必然存在scrollView,可以一级级的查找,本次使用该方法;

【查找scrollView的源码】

【滚动动作】在动画结束之后进行滚动;


【增加箭头的切换】


【BUG】本身文字的高度不到7行,现在显示的还是7行,产生了空白;

[原因]系统也是需要事件缓冲的,直接运行无法直接响应;


[测试效果]展示3行了;

=====自定义排行控件=======
3.自定义排行控件原理分析

【分析】会出现的几种情况

4.自定义排行控件的onMeasure测量
【获取有效宽高/测量子控件的宽度】

4.1 第一种情况:增加子控件超边界需要换行
【对已经占据的宽度记录判断】

【水平控件之间的间距】

【超出边界与否的判断】

【换行对象】

【为当前行增加子控件】


【换行类生成】





【增加换行功能】第一个情况解决;

4.2 超出边界的考虑

【超出边界的考虑】

【获取孩子的数量】

【超出边界的分类】

【第三种情况的处理】控件很长,强制添加,然后换行;属于else的情况1;


【第二种情况的处理】先换行,再添加,宽度累计增加;


【将最后一行的数据添加后集合中】在newLine中保存的是上一行的数据,在for循环结束之后,最后一行的控件可能无法添加;


【根据尺寸重新绘制】




【onMeasure方法源码】
1 package com.itheima.googleplay74.ui.view; 2 3 import java.util.ArrayList; 4 5 import com.itheima.googleplay74.utils.UIUtils; 6 7 import android.content.Context; 8 import android.util.AttributeSet; 9 import android.view.View; 10 import android.view.ViewGroup; 11 12 public class MyFlowLayout extends ViewGroup { 13 14 private int mUsedWidth;// 当前行已使用的宽度 15 private int mHorizontalSpacing = UIUtils.dip2px(6);// 水平间距 16 private int mVerticalSpacing = UIUtils.dip2px(8);// 竖直间距 17 18 private Line mLine;// 当前行对象 19 20 private ArrayList<Line> mLineList = new ArrayList<MyFlowLayout.Line>();// 维护所有行的集合 21 22 private static final int MAX_LINE = 100;// 最大行数是100行 23 24 public MyFlowLayout(Context context, AttributeSet attrs, int defStyle) { 25 super(context, attrs, defStyle); 26 } 27 28 public MyFlowLayout(Context context, AttributeSet attrs) { 29 super(context, attrs); 30 } 31 32 public MyFlowLayout(Context context) { 33 super(context); 34 }
50 @Override 51 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 52 // 获取有效宽度 53 int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() 54 - getPaddingRight(); 55 // 获取有效高度 56 int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() 57 - getPaddingBottom(); 58 59 // 获取宽高模式 60 int widthMode = MeasureSpec.getMode(widthMeasureSpec); 61 int heightMode = MeasureSpec.getMode(heightMeasureSpec); 62 63 int childCount = getChildCount();// 获取所有子控件数量 64 for (int i = 0; i < childCount; i++) { 65 View childView = getChildAt(i); 66 67 // 如果父控件是确定模式, 子控件包裹内容;否则子控件模式和父控件一致 68 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, 69 (widthMode == MeasureSpec.EXACTLY) ? MeasureSpec.AT_MOST 70 : widthMode); 71 int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 72 (heightMode == MeasureSpec.EXACTLY) ? MeasureSpec.AT_MOST 73 : heightMode); 74 75 // 开始测量 76 childView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 77 78 // 如果当前行对象为空, 初始化一个行对象 79 if (mLine == null) { 80 mLine = new Line(); 81 } 82 83 // 获取子控件宽度 84 int childWidth = childView.getMeasuredWidth(); 85 86 mUsedWidth += childWidth;// 已使用宽度增加一个子控件宽度 87 88 if (mUsedWidth < width) {// 判断是否超出了边界 89 mLine.addView(childView);// 更当前行对象添加子控件 90 91 mUsedWidth += mHorizontalSpacing;// 增加一个水平间距 92 93 if (mUsedWidth > width) { 94 // 增加水平间距之后, 就超出了边界, 此时需要换行 95 if (!newLine()) { 96 break;// 如果创建行失败,就结束循环,不再添加 97 } 98 } 99 100 } else { 101 // 已超出边界 102 // 1.当前没有任何控件, 一旦添加当前子控件, 就超出边界(子控件很长) 103 if (mLine.getChildCount() == 0) { 104 mLine.addView(childView);// 强制添加到当前行 105 106 if (!newLine()) {// 换行 107 break; 108 } 109 } else { 110 // 2.当前有控件, 一旦添加, 超出边界 111 if (!newLine()) {// 换行 112 break; 113 } 114 115 mLine.addView(childView); 116 mUsedWidth += childWidth + mHorizontalSpacing;// 更新已使用宽度 117 } 118 } 119 120 } 121 122 // 保存最后一行的行对象 123 if (mLine != null && mLine.getChildCount() != 0 124 && !mLineList.contains(mLine)) { 125 mLineList.add(mLine); 126 } 127 128 int totalWidth = MeasureSpec.getSize(widthMeasureSpec);// 控件整体宽度 129 130 int totalHeight = 0;// 控件整体高度 131 for (int i = 0; i < mLineList.size(); i++) { 132 Line line = mLineList.get(i); 133 totalHeight += line.mMaxHeight; 134 } 135 136 totalHeight += (mLineList.size() - 1) * mVerticalSpacing;// 增加竖直间距 137 totalHeight += getPaddingTop() + getPaddingBottom();// 增加上下边距 138 139 // 根据最新的宽高来测量整体布局的大小 140 setMeasuredDimension(totalWidth, totalHeight); 141 // super.onMeasure(widthMeasureSpec, heightMeasureSpec); 142 } 143 144 // 换行 145 private boolean newLine() { 146 mLineList.add(mLine);// 保存上一行数据 147 148 if (mLineList.size() < MAX_LINE) { 149 // 可以继续添加 150 mLine = new Line(); 151 mUsedWidth = 0;// 已使用宽度清零 152 153 return true;// 创建行成功 154 } 155 156 return false;// 创建行失败 157 } 158 159 // 每一行的对象封装 160 class Line { 161 162 private int mTotalWidth;// 当前所有控件总宽度 163 public int mMaxHeight;// 当前控件的高度(以最高的控件为准) 164 165 private ArrayList<View> mChildViewList = new ArrayList<View>();// 当前行所有子控件集合 166 167 // 添加一个子控件 168 public void addView(View view) { 169 mChildViewList.add(view); 170 // 总宽度增加 171 mTotalWidth += view.getMeasuredWidth(); 172 173 int height = view.getMeasuredHeight(); 174 // 0 10 20 10 175 mMaxHeight = mMaxHeight < height ? height : mMaxHeight; 176 } 177 178 public int getChildCount() { 179 return mChildViewList.size(); 180 }
5.自定义排行控件的onLayout布局
【思路】每一行的对象控制好各自各行的小控件,然后在Activity中控制好每一行;

5.1 每行的子控件的位置的布局
【说明】需要知道每个子控件的左上角的坐标(left,top),然后子控件知道自己的宽高,就可以画出来了;
【存在剩余空间的情况】【需要考虑的特殊情况】最后黄色布局剩余了空间;


【修改】需要改为确定的值;

【控件高度不一致的情况的画法】


【考虑第二个控件的横向尺寸值】增加前一个控件的横向尺寸值和一个横向的边距值;


【出现负值的情况】


5.2 大布局中的每行的排列

【修改使用到项目中】

【测试】效果与之前的一致;

6.Actionbar的使用

6.1 对应的加载布局


6.2 属性值的含义
【orderInCategory】当前的item属于的级别,可能存在多个item,各有级别;


【showAsaction】展现的方式

[always]一直展现出来;


[never]永远都是隐藏,需要点击才会显示出来;

[ifroom]如果空间容许的话就展现;


6.3 展示图片

【长按可以弹出土司】可以通过长按点击可以判断是否为ActionBar开发的;微信的搜索按钮可以长按弹吐司;

【ActionBar的点击事件处理】


【修改标题】


【设置图标】


【设置图标是否可以被点击】true:可以被点击;false:不可以被点击;


【隐藏图标】


【显示返回键】


【返回按键的事件响应】


7.使用Actionbar改造电子市场&侧边栏效果DrawerLayout
7.1 效果及思路
【思路】点击按钮之后会出现侧边栏,侧边栏使用的是另外一种方式,谷歌原生的效果,DrawerLayout;

7.2 侧边栏的构造
【说明】使用下面的谷歌提供的DrawerLayout侧边栏效果的控件;


【谷歌提供的DrawerLayout侧边栏效果的控件】类似于帧布局,将侧边栏和内容页重叠在了一起;

【初始化抽屉数据】



【该方法调用的时机】

【拦截点击图标的事件】


【在详情页面添加返回键】



7.3 点评ActionBar
【开源中国中使用的比较多】
2.
浙公网安备 33010602011771号