【Android群英传】——第三章:Android控件架构与自定
2020-01-28 本文已影响0人
感同身受_
之前学习了《Android群英传》,不过当时时间很零碎,很多问题没有深究、理解,趁着放假,认真回顾了之前的代码,同时解决了之前没有解决的问题,这里只是记录一下写Demo过程中遇到的问题及解决办法,具体笔记都在书上(感觉书上记录笔记方便一点),源代码放在我的GitHub
上
3.2 View的测量
- 重写
onMeasure
方法,这里自己调用setMeasuredDimension
方法,就不需要将测量值传到super.onMeasure()
中,在View里面去调用setMeasuredDimension
方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}
- 测量长宽的模板代码
private int measureWidth(int measureSpec){
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);//得到测量模式
int specSize = MeasureSpec.getSize(measureSpec);//得到测量值
if (specMode == MeasureSpec.EXACTLY){
result = specSize;
}else{//若不为EXACTLY模式,则设置默认大小
result = 200;
if (specMode == MeasureSpec.AT_MOST){
result = Math.min(result,specSize);
}
}
return result;
}
3.6 自定义View
- 闪动的文字效果
这里继承TextView
的时候,会报错,但是运行不会受影响
)布局:
<com.example.systemwidgettest.MyTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_below="@+id/myTopBar"
android:text="Android"
android:textSize="28sp"
android:layout_centerHorizontal="true"/>
2)代码:
闪动的文字效果
- 创建复合控件
1)布局:
<com.example.systemwidgettest.MyTopBar
android:id="@+id/myTopBar"
android:layout_width="wrap_content"
android:layout_height="40dp"
custom:leftBackground="@drawable/ic_launcher_background"
custom:leftText="Back"
custom:leftTextColor="#FFFFFF"
custom:rightBackground="@drawable/ic_launcher_background"
custom:rightText="More"
custom:rightTextColor="#FFFFFF"
custom:title="消息"
custom:titleTextColor1="#CA1212"
custom:titleTextSize="10sp"/>
2)代码:
创建复合控件
- 重写View来实现全新的控件
1)布局:
<com.example.systemwidgettest.MyCanvas
android:id="@+id/myCanvas"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/myTopBar"/>
2)代码:
重写View来实现全新的控件
- 音频条形图
1)布局:
<com.example.systemwidgettest.MyAudio
android:id="@+id/myAudio"
android:layout_below="@+id/myTopBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
2)代码:
音频条形图
3.7 自定义ViewGroup
1)布局:
<com.example.systemwidgettest.MyViewGroup
android:id="@+id/myViewGroup"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/bg" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/bg2" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/bg3" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/bg4" />
</com.example.systemwidgettest.MyViewGroup>
2)代码:
自定义ViewGroup
-
child.layout(l,i*mScreenHeight,r,(i+1)*mScreenHeight);
写这个方法时,第二个参数我没有写i*
,导致界面无法显示
@Override
public void onLayout(boolean changed,int l,int t,int r,int b){
int childCount = getChildCount();
Log.d(TAG, "onLayout: childCount: "+childCount);//有n个子view
//设置ViewGroup的高度
MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
mlp.height = mScreenHeight*childCount;
Log.d(TAG, "onLayout: mScreenHeight: "+mScreenHeight);
setLayoutParams(mlp);
//将子view放到viewGroup中
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE){
//ViewGroup在执行Layout的过程中,通过遍历来调用View的Layout方法,并传入参数确定其位置
child.layout(l,i*mScreenHeight,r,(i+1)*mScreenHeight);
}
}
}
- 在onTouchEvent()方法中,在
case MotionEvent.ACTION_MOVE:
分支上,书上写的是if(getScaleY()>getHeight()-mScreenHeight)
判断为>
,但是实际应该为<
才能正常运行,原因写在代码上了
//实现ViewGroup的触控事件和滑动事件
@Override
public boolean onTouchEvent(MotionEvent event){
int y = (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mLastY = y;
mStart = getScrollY();//手指起始位置
break;
case MotionEvent.ACTION_MOVE:
if(!mScroller.isFinished()){
mScroller.abortAnimation();
}
int dy = mLastY-y;
if(getScrollY()<0){
dy = 0;
}
//之前得判断为getScaleY()>getHeight()-mScreenHeight
//这段代码加上后,向下滑,不能滑动,因为getScaleY()方法返回为1
//getHeight()方法返回大于2621,mScreenHeight=2621
//所以这个判断恒为真
//所以我觉得是getScaleY()<getHeight()-mScreenHeight
if(getScaleY()<getHeight()-mScreenHeight){
dy = 0;
}
scrollBy(0,dy);//手指滑动时让viewGroup的所有子view跟着滑动 dy 距离
mLastY = y;
break;
case MotionEvent.ACTION_UP:
mEnd = getScrollY();//记录触摸终点
// int dScrollY = (int) (mEnd - mStart);//得到手指滑动距离
int dScrollY = checkAlignment();
if (dScrollY>0) {
if (dScrollY<mScreenHeight/3){//小于一定距离
mScroller.startScroll(0, (int) getScrollY(),0,-dScrollY);//回到原来的位置
}else{//若超过一定距离
mScroller.startScroll(0, (int) getScrollY(),0,mScreenHeight-dScrollY);//使用Scroller类平滑到下一个子view
}
}else{
if(-dScrollY<mScreenHeight/3){
mScroller.startScroll(0, (int) getScrollY(),0,-dScrollY);
}else{
mScroller.startScroll(0, (int) getScrollY(),0,-mScreenHeight-dScrollY);
}
}
break;
}
postInvalidate();
return true;//将继续向上传递审核
}
- 在
case MotionEvent.ACTION_UP:
分支上,我把getScrollY()
方法,写成了getScaleY()
方法,导致返回的结果始终为1(因为getScaleY()
方法返回的是缩放值,但是我没有对他进行缩放操作,所以返回的缩放值始终是100%,即1),getScrollY()
是返回视图的滚动顶部位置,是值位于[0,mScreenHeight]的。
case MotionEvent.ACTION_UP:
mEnd = getScrollY();//记录触摸终点
// int dScrollY = (int) (mEnd - mStart);//得到手指滑动距离
int dScrollY = checkAlignment();
if (dScrollY>0) {
if (dScrollY<mScreenHeight/3){//小于一定距离
mScroller.startScroll(0, (int) getScrollY(),0,-dScrollY);//回到原来的位置
}else{//若超过一定距离
mScroller.startScroll(0, (int) getScrollY(),0,mScreenHeight-dScrollY);//使用Scroller类平滑到下一个子view
}
}else{
if(-dScrollY<mScreenHeight/3){
mScroller.startScroll(0, (int) getScrollY(),0,-dScrollY);
}else{
mScroller.startScroll(0, (int) getScrollY(),0,-mScreenHeight-dScrollY);
}
}
break;