IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android自定义View,2021华为Android面试真题 -> 正文阅读

[移动开发]Android自定义View,2021华为Android面试真题

     bitmapCanvas.drawPath(getPathAFromLowerRight(),pathAPaint);
    canvas.drawBitmap(bitmap,0,0,null);
}

/**
 * 获取f点在右下角的pathA
 * @return
 */
private Path getPathAFromLowerRight(){
    pathA.reset();
    pathA.lineTo(0, viewHeight);//移动到左下角
    pathA.lineTo(c.x,c.y);//移动到c点
    pathA.quadTo(e.x,e.y,b.x,b.y);//从c到b画贝塞尔曲线,控制点为e
    pathA.lineTo(a.x,a.y);//移动到a点
    pathA.lineTo(k.x,k.y);//移动到k点
    pathA.quadTo(h.x,h.y,j.x,j.y);//从k到j画贝塞尔曲线,控制点为h
    pathA.lineTo(viewWidth,0);//移动到右上角
    pathA.close();//闭合区域
    return pathA;
}

}


效果如图

![](https://user-gold-cdn.xitu.io/2017/12/14/16053a51342e05a9?imageView2/0/w/1280/h/960/ignore-error/1)

**区域C**理论上应该是由点**a**,**b**,**d**,**i**,**k**连接而成的闭合区域,但由于**d**和**i**是曲线上的点,我们没办法直接从**d**出发通过**path**绘制路径连接**b**点(**i**,**k**同理),也就不能只用**path**的情况下直接绘制出**区域C**,我们需要用**PorterDuffXfermode**方面的知识“曲线救国”。我们试着先将点**a**,**b**,**d**,**i**,**k**连接起来,观察闭合区域与**区域A**之间的联系。修改**BookPageView**

private void init(Context context, @Nullable AttributeSet attrs){
//省略部分代码…
pathCPaint = new Paint();
pathCPaint.setColor(Color.YELLOW);
pathCPaint.setAntiAlias(true);//设置抗锯齿

pathC = new Path();

}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

bitmap = Bitmap.createBitmap((int) viewWidth, (int) viewHeight, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
bitmapCanvas.drawPath(getPathAFromLowerRight(),pathAPaint);
bitmapCanvas.drawPath(getPathC(),pathCPaint);
canvas.drawBitmap(bitmap,0,0,null);

}

/**

  • 绘制区域C
  • @return
    */
    private Path getPathC(){
    pathC.reset();
    pathC.moveTo(i.x,i.y);//移动到i点
    pathC.lineTo(d.x,d.y);//移动到d点
    pathC.lineTo(b.x,b.y);//移动到b点
    pathC.lineTo(a.x,a.y);//移动到a点
    pathC.lineTo(k.x,k.y);//移动到k点
    pathC.close();//闭合区域
    return pathC;
    }

效果如图

![](https://user-gold-cdn.xitu.io/2017/12/14/16053a51328cc528?imageView2/0/w/1280/h/960/ignore-error/1)

我们将两条曲线也画出来对比观察

![](https://user-gold-cdn.xitu.io/2017/12/14/16053a5158cd3dc7?imageView2/0/w/1280/h/960/ignore-error/1)

观察分析后可以得出结论,**区域C**是 **由直线ab,bd,dj,ik,ak连接而成的区域** 减去 **与区域A交集部分** 后剩余的区域。于是我们设置**区域C**画笔**Xfermode**模式为**DST\_ATOP**

pathCPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));


效果如图

![](https://user-gold-cdn.xitu.io/2017/12/14/16053a515892fd66?imageView2/0/w/1280/h/960/ignore-error/1)

最后是**区域B**,因为**区域B**处于最底层,我们直接将**区域B**画笔**Xfermode**模式设为**DST\_ATOP**,在**区域A、C**之后绘制即可,修改**BookPageView**

private void init(Context context, @Nullable AttributeSet attrs){
//省略部分代码…
pathBPaint = new Paint();
pathBPaint.setColor(getResources().getColor(R.color.blue_light));
pathBPaint.setAntiAlias(true);//设置抗锯齿
pathBPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));

pathB = new Path();

}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//省略部分代码…
bitmap = Bitmap.createBitmap((int) viewWidth, (int) viewHeight, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
bitmapCanvas.drawPath(getPathAFromLowerRight(),pathAPaint);
bitmapCanvas.drawPath(getPathC(),pathCPaint);
bitmapCanvas.drawPath(getPathB(),pathBPaint);
canvas.drawBitmap(bitmap,0,0,null);
}

/**

  • 绘制区域B
  • @return
    */
    private Path getPathB(){
    pathB.reset();
    pathB.lineTo(0, viewHeight);//移动到左下角
    pathB.lineTo(viewWidth,viewHeight);//移动到右下角
    pathB.lineTo(viewWidth,0);//移动到右上角
    pathB.close();//闭合区域
    return pathB;
    }

效果如图

![](https://user-gold-cdn.xitu.io/2017/12/14/16053a515c1389b5?imageView2/0/w/1280/h/960/ignore-error/1)

翻页可以从右下方翻自然也可以从右上方翻,我们将**f**点设在右上角,由于View上下两部分是呈**镜像**的,所以各标识点的位置也应该是镜像对应的,因为**区域B和C**的绘制与**f**点没有关系,所以我们只需要修改**区域A**的绘制逻辑,新增**getPathAFromTopRight**方法

public class BookPageView extends View {
//省略部分代码…
private void init(Context context, @Nullable AttributeSet attrs){
a = new MyPoint(400,200);
f = new MyPoint(viewWidth,0);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
	//省略部分代码...
    bitmap = Bitmap.createBitmap((int) viewWidth, (int) viewHeight, Bitmap.Config.ARGB_8888);
    bitmapCanvas = new Canvas(bitmap);

// bitmapCanvas.drawPath(getPathAFromLowerRight(),pathAPaint);
bitmapCanvas.drawPath(getPathAFromTopRight(),pathAPaint);
bitmapCanvas.drawPath(getPathC(),pathCPaint);
bitmapCanvas.drawPath(getPathB(),pathBPaint);
}

/**
 * 获取f点在右上角的pathA
 * @return
 */
private Path getPathAFromTopRight(){
    pathA.reset();
    pathA.lineTo(c.x,c.y);//移动到c点
    pathA.quadTo(e.x,e.y,b.x,b.y);//从c到b画贝塞尔曲线,控制点为e
    pathA.lineTo(a.x,a.y);//移动到a点
    pathA.lineTo(k.x,k.y);//移动到k点
    pathA.quadTo(h.x,h.y,j.x,j.y);//从k到j画贝塞尔曲线,控制点为h
    pathA.lineTo(viewWidth,viewHeight);//移动到右下角
    pathA.lineTo(0, viewHeight);//移动到左下角
    pathA.close();
    return pathA;
}

}


效果如图

![](https://user-gold-cdn.xitu.io/2017/12/14/16053a515bdc35a8?imageView2/0/w/1280/h/960/ignore-error/1)

* * *

测量及自适应View的宽高
-------------

> ### 相关博文链接
> 
> [浅谈自定义View的宽高获取](
)


> 
> [教你搞定Android自定义View](
)



之前由于测试效果没有对View的大小进行重新测量,在实现触摸翻页之前先把这个结了。重写View的**onMeasure**方法

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = measureSize(defaultHeight, heightMeasureSpec);
int width = measureSize(defaultWidth, widthMeasureSpec);
setMeasuredDimension(width, height);

viewWidth = width;
viewHeight = height;
f.x = width;
f.y = height;
calcPointsXY(a,f);//将初始化计算放在这

}

private int measureSize(int defaultSize,int measureSpec) {
int result = defaultSize;
int specMode = View.MeasureSpec.getMode(measureSpec);
int specSize = View.MeasureSpec.getSize(measureSpec);

if (specMode == View.MeasureSpec.EXACTLY) {
	result = specSize;
} else if (specMode == View.MeasureSpec.AT_MOST) {
	result = Math.min(result, specSize);
}
return result;

}


* * *

通过触摸控制各标识点位置
------------

我们的需求是,在上半部分翻页时**f**点在右上角,在下半部分翻页时**f**则在右下角,当手指离开屏幕时回到**初始状态**,根据需求,修改**BookPageView**

public class BookPageView extends View {
//省略部分代码…
public static final String STYLE_TOP_RIGHT = “STYLE_TOP_RIGHT”;//f点在右上角
public static final String STYLE_LOWER_RIGHT = “STYLE_LOWER_RIGHT”;//f点在右下角

private void init(Context context, @Nullable AttributeSet attrs){ 
	//省略部分代码...
    a = new MyPoint();
    f = new MyPoint();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int height = measureSize(defaultHeight, heightMeasureSpec);
    int width = measureSize(defaultWidth, widthMeasureSpec);
    setMeasuredDimension(width, height);

    viewWidth = width;
    viewHeight = height;
    a.x = -1;
    a.y = -1;
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    bitmap = Bitmap.createBitmap((int) viewWidth, (int) viewHeight, Bitmap.Config.ARGB_8888);
    bitmapCanvas = new Canvas(bitmap);
    if(a.x==-1 && a.y==-1){
        bitmapCanvas.drawPath(getPathDefault(),pathAPaint);
    }else {
        if(f.x==viewWidth && f.y==0){
            bitmapCanvas.drawPath(getPathAFromTopRight(),pathAPaint);
        }else if(f.x==viewWidth && f.y==viewHeight){
            bitmapCanvas.drawPath(getPathAFromLowerRight(),pathAPaint);
        }

        bitmapCanvas.drawPath(getPathC(),pathCPaint);
        bitmapCanvas.drawPath(getPathB(),pathBPaint);
    }
    canvas.drawBitmap(bitmap,0,0,null);
}

/**
 * 设置触摸点
 * @param x
 * @param y
 * @param style
 */
public void setTouchPoint(float x, float y, String style){
    switch (style){
        case STYLE_TOP_RIGHT:
            f.x = viewWidth;
            f.y = 0;
            break;
        case STYLE_LOWER_RIGHT:
            f.x = viewWidth;
            f.y = viewHeight;
            break;
        default:
            break;
    }
    a.x = x;
    a.y = y;
    calcPointsXY(a,f);
    postInvalidate();
}

/**
 * 回到默认状态
 */
public void setDefaultPath(){
    a.x = -1;
    a.y = -1;
    postInvalidate();
}

/**
 * 绘制默认的界面
 * @return
 */
private Path getPathDefault(){
    pathA.reset();
    pathA.lineTo(0, viewHeight);
    pathA.lineTo(viewWidth,viewHeight);
    pathA.lineTo(viewWidth,0);
    pathA.close();
    return pathA;
}

public float getViewWidth(){
    return viewWidth;
}

public float getViewHeight(){
    return viewHeight;
}

}


在Activity中监听View的**onTouch**状态

bookPageView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
if(event.getY() < bookPageView.getViewHeight()/2){//从上半部分翻页
bookPageView.setTouchPoint(event.getX(),event.getY(),bookPageView.STYLE_TOP_RIGHT);
}else if(event.getY() >= bookPageView.getViewHeight()/2) {//从下半部分翻页
bookPageView.setTouchPoint(event.getX(),event.getY(),bookPageView.STYLE_LOWER_RIGHT);
}
break;
case MotionEvent.ACTION_MOVE:
bookPageView.setTouchPoint(event.getX(),event.getY(),"");
break;
case MotionEvent.ACTION_UP:
bookPageView.setDefaultPath();//回到默认状态
break;
}
return false;
}
});


注意,要设置**android:clickable**为**true**,否则无法监听到**ACTION\_MOVE**和**ACTION\_UP**状态

<com.anlia.pageturn.BookPageView
android:id="@+id/view_book_page"
android:layout_width=“300dp”
android:layout_height=“450dp”
android:layout_marginLeft=“15dp”
android:layout_marginTop=“15dp”
android:clickable=“true”/>


效果如图

![](https://user-gold-cdn.xitu.io/2017/12/14/16053a5164a04ce5?imageslim)

到这里我们已经实现了基本的翻页效果,但要还原真实的书籍翻页效果,我们还需要设置一些限制条件来完善我们的项目

* * *

限制右侧翻页的最大距离
-----------

对于一般的书本来说,最左侧应该是钉起来的,也就是说如果我们从右侧翻页,翻动的距离是**有限制的**,最下方翻页形成的曲线起点(**c**点)的x坐标不能小于0(上方同理),按照这个限定条件,修改我们的**BookPageView**

/**

  • 设置触摸点
  • @param x
  • @param y
  • @param style
    /
    public void setTouchPoint(float x, float y, String style){
    switch (style){
    case STYLE_TOP_RIGHT:
    f.x = viewWidth;
    f.y = 0;
    break;
    case STYLE_LOWER_RIGHT:
    f.x = viewWidth;
    f.y = viewHeight;
    break;
    default:
    break;
    }
    MyPoint touchPoint = new MyPoint(x,y);
    //如果大于0则设置a点坐标重新计算各标识点位置,否则a点坐标不变
    if(calcPointCX(touchPoint,f)>0){
    a.x = x;
    a.y = y;
    calcPointsXY(a,f);
    }else {
    calcPointsXY(a,f);
    }
    postInvalidate();
    }
    /
    *
  • 计算C点的X值
  • @param a
  • @param f
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-29 09:28:21  更:2021-08-29 09:29:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:34:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码