Android高级UI面试题汇总(含详细解析 九)

62 阅读7分钟

Android并发编程高级面试题汇总最全最细面试题讲解持续更新中👊👊 👀你想要的面试题这里都有👀 👇👇👇

Requestlayout,onlayout,onDraw,DrawChild区别与联系

这道题想考察什么?

考察同学对这几个方法背后的执行原理是否熟悉。

考生应该如何回答

讲解他们的工作流程,还有运行后的效果,从而对比他们的区别和联系

我们先来看View中的RequestLayout,看名字大家应该不难判断这个方法就是请求重新布局。

public void requestLayout() {
    //清空测量缓存
    if (mMeasureCache != null) mMeasureCache.clear();
    ...
    //添加强制layout 标记,该标记触发layout
    mPrivateFlags |= PFLAG_FORCE_LAYOUT;
    //添加重绘标记
    mPrivateFlags |= PFLAG_INVALIDATED;
​
    if (mParent != null && !mParent.isLayoutRequested()) {
        //如果上次的layout 请求已经完成
        //父布局继续调用requestLayout
        mParent.requestLayout();
    }
    ...
}

不难看出,这个递归调用和题目7.10 中invalidate是采用了一样的配方,向上寻找其父布局,一直到ViewRootImpl为止,给每个布局设置PFLAG_FORCE_LAYOUT和PFLAG_INVALIDATED标记,注意这个是关键,所有的布局都在递归中被设置了标志位,然后最终会走到ViewRootImpl里面的requestLayout(),看下面的代码:

public void requestLayout() {
    //是否正在进行layout过程
    if (!mHandlingLayoutInLayoutRequest) {
        //检查线程是否一致
        checkThread();
        //标记有一次layout的请求
        mLayoutRequested = true;
        //开启View 三大流程
        scheduleTraversals();
    }
}

所以,RequestLayout做的具体事情就是这样的:向上寻找父布局,找到后给他们标上强制layout和重绘的标签;然后 调用ViewRootImpl的 scheduleTraversals()开启三大绘制流程。

总结

RequestLayout()方法:会导致调用 onMeasure()方法和 onLayout(),将会根据标志位判断是否 需要 onDraw(); onLayout():摆放 viewGroup 里面的子控件,只有自定义ViewGroup需要重写; onDraw():绘制视图本身(ViewGroup 还需要绘制里面的所有子控件); drawChild(): 重新回调每一个子视图的 draw 方法 ,child.draw(canvas, this, drawingTime);

View的滑动方式

详细讲解

享学课堂系列课:高级UI专题:事件分发源码解析课程

这道题想考察什么?

Android中经常看到一些炫酷的效果,这些效果很多伴随着View的滑动。我们想要做出这样的效果,掌握View的滑动方式必不可少。

考察的知识点

View的滑动方式的概念在项目中使用及相关基本原理的理解。

考生应该如何回答

我们在使用View的过程中,经常需要实现View的滑动效果。比如ListView、跟随手指而移动的自定义View等等,前者的滑动效果是SDK为我们提供的,而对于我们自定义View的滑动效果就需要我们自己来实现,下面介绍四种滑动的方式,和大家一起共享一下。另外,在回答这个问题的时候,希望大家也去学习一下 View 的位置相关内容,View的坐标相关内容。

1、使用scrollTo/scrollBy

为了实现View滑动,Android专门提供了这两个方法让我们使用。这两个函数的区别是scrollBy提供的是基于当前位置的相对滑动,而scrollTo提供的是基于起始位置的绝对滑动。

需要注意的是实际的滑动方向与我们想当然的方向不同,这个问题与View的内部变量mScrollX和mScrollY的含义有关,scrollTo函数与scrollBy函数实际上就是对这两个变量做出修改。

mScrollX:View的左边缘坐标减去View内容的左边缘坐标。

mScrollY:View的上边缘坐标减去View内容的上边缘坐标。

另外一个需要注意的地方是超出View边缘范围的内容是不会显示的。

img

2、使用动画

动画的方式主要是操作View的translationX与translationY属性。可以使用XML文件自定义动画效果也可以使用属性动画实现。

2.1、自定义动画

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
    <translate
        android:duration="100"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="100"
        android:toYDelta="0"
        android:interpolator="@android:anim/linear_interpolator"/>
</set>
View view = findViewById(R.id.target_view);
Animation animation = AnimationUtils.loadAnimation(context, R.anim.target_animation);
view.startAnimation(animation);

注意在XML文件中有一个fillAfter属性,如果设置为false的话当动画结束时View会恢复到原来的位置。

2.2、属性动画

View view = findViewById(R.id.target_view);
ObjectAnimator.ofFloat(view, "translationX", 0, 100)
    .setDuration(100)
    .start();

使用动画来实现View的滑动思想:主要通过改变View的translationX和translationY参数来实现,使用动画的好处在于滑动效果是平滑的。

3、改变布局参数

第三种方法是改变View的LayoutParams,与之前的方法相比,这种方法显得不是很正统,但是也可以实现我们的需求。举个例子,假如我们想把一个View右移10dp,那么最简单的方式就是把它LayoutParams里的marginLeft参数的值增加10dp。但这种方法要根据View所在的父布局灵活调整,在一些情况下改变margin值并不能改变View的位置。

View view = findViewById(R.id.target_view);
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view .getLayoutParams();
params.leftMargin += 10;
mTargetTextView.setLayoutParams(params);

还有一个相似的方法,但可用性要好一些,那就是调用View的layout方法,直接修改View相对于父布局的位置。

int offsetX = 10;
View view = findViewById(R.id.target_view);
view.layout(mTargetTextView.getLeft() + offsetX,
            view.getTop(),
            view.getRight() + offsetX,
            view.getBottom());

通过改变布局参数来实现View的滑动的思想很简单:比如向右移动一个View,只需要把它的marginLeft参数增大,向其它方向移动同理,只需改变相应的margin参数;还有一种比较拐弯抹角的方法是在要移动的View的旁边预先放一个View(初始宽高设为0),然后比如我们要向右移动View,只需把预先放置的那个View的宽度增大,这样就把View“挤”到右边了。

4、使用Scroller实现渐进式滑动

上边提到的滑动方式有一个共同的缺点那是他们都不是渐进式的滑动。实现渐进式滑动的共同思想就是将一个滑动行为分解为若干个,在一段时间内按顺序完成。

这里介绍使用Scroller类的实现方式。

public class ScrollerTextView extends TextView {
​
    private Scroller mScroller;
​
    public ScrollerTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
    }
​
    public void smoothScrollBy(int dx, int dy) {
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        mScroller.startScroll(scrollX, scrollY, dx, dy);
        invalidate();
    }
​
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }
}

大概了解下Scroller实现弹性滑动的原理:invaldate方法会导致View的draw方法被调用,而draw会调用computeScroll方法,因此重写了computeScroll方法,而computeScrollOffset方法会根据时间的流逝动态的计算出很小的一段时间应该滑动多少距离。也就是把一次滑动拆分成无数次小距离滑动从而实现“弹性滑动”。

更多Android面试题 可以详细Vx关注公众号:Android老皮 解锁       《2023最新Android中高级面试题汇总+解析》

目录

img

第一章 Java方面

  • Java基础部分
  • Java集合
  • Java多线程
  • Java虚拟机

img

第二章 Android方面

  • Android四大组件相关
  • Android异步任务和消息机制
  • Android UI绘制相关
  • Android性能调优相关
  • Android中的IPC
  • Android系统SDK相关
  • 第三方框架分析
  • 综合技术
  • 数据结构方面
  • 设计模式
  • 计算机网络方面
  • Kotlin方面

img

第三章 音视频开发高频面试题

  • 为什么巨大的原始视频可以编码成很小的视频呢?这其中的技术是什么呢?
  • 怎么做到直播秒开优化?
  • 直方图在图像处理里面最重要的作用是什么?
  • 数字图像滤波有哪些方法?
  • 图像可以提取的特征有哪些?
  • 衡量图像重建好坏的标准有哪些?怎样计算?

img

第四章 Flutter高频面试题

  • Dart部分
  • Flutter部分

img

第五章 算法高频面试题

  • 如何高效寻找素数
  • 如何运用二分查找算法
  • 如何高效解决雨水问题
  • 如何去除有序数组的重复元素
  • 如何高效进行模幂运算
  • 如何寻找最长回文子串

img

第六章 Andrio Framework方面

  • 系统启动流程面试题解析
  • Binder面试题解析
  • Handler面试题解析
  • AMS面试题解析