精华内容
下载资源
问答
  • 前言自定义View 是 Android 开发者必须了解的基础网上有大量关于自定义View 原理的文章,但存在一些问题:内容不全、思路不清晰、无源码分析、简单问题复杂化等等今天,我将全面总结自定义View 的原理,我能保证这是...

    前言

    • 自定义View 是 Android 开发者必须了解的基础
    • 网上有大量关于自定义View 原理的文章,但存在一些问题:内容不全、思路不清晰、无源码分析、简单问题复杂化等等
    • 今天,我将全面总结自定义View 的原理,我能保证这是市面上的最全面、最清晰、最易懂的

    本文秉着“结论先行、详细分析在后”的原则,即先让大家感性认识,再通过理性分析从而理解问题; 所以,请各位读者先记住结论,再往下继续看分析; 文章较长,阅读需要较长时间,建议收藏等充足时间再进行阅读


    目录

    f4fae57dcf52cb790de8fe73cd901548.png

    1. 储备知识

    1.1 ViewRoot

    • 定义 连接器,对应于ViewRootImpl类
    • 作用 连接WindowManager 和 DecorView 完成View的三大流程: measure、layout、draw
    • 特别注意
    // 在主线程中,Activity对象被创建后:// 1. 自动将DecorView添加到Window中 & 创建ViewRootImpll对象root = new ViewRootImpl(view.getContent(),display);// 3. 将ViewRootImpll对象与DecorView建立关联root.setView(view,wparams,panelParentView)

    1.2 DecorView

    • 定义:顶层View

    即 Android 视图树的根节点;同时也是 FrameLayout 的的类

    • 作用:显示 & 加载布局

    View层的事件都先经过DecorView,再传递到View

    • 特别说明 内含1个竖直方向的LinearLayout,分为2部分:上 = 标题栏(titlebar)、下 = 内容栏(content)
    32b273d4fa194b017046c0217b098014.png

    在Activity中通过 setContentView()所设置的布局文件其实是被加到内容栏之中的,成为其唯一子View = id为content的FrameLayout中

    // 在代码中可通过content得到对应加载的布局// 1. 得到contentViewGroup content = (ViewGroup)findViewById(android.R.id.content);// 2. 得到设置的ViewViewGroup rootView = (ViewGroup) content.getChildAt(0);

    1.3 Window、Activity、DecorView 与 ViewRoot的关系

    • 简介
    3173b5d6ff3879dfac7e783dc0bf17b4.png
    • 之间的关系
    1dc7a568735bed4b7eaa655e33b99f19.png
    • 更加详细 & 具体的介绍,请看文章:Android自定义View基础:ViewRoot、DecorView & Window的简介

    1.4 自定义View基础

    了解自定义View流程前,需了解一定的自定义View基础


    2. 绘制准备

    • 回忆上图,可看出最后1步 = 绘制
    7c2b439ed01e210c79d858b851175397.png
    • 但在绘制前,系统会有一些绘制准备,即前面几个步骤:创建PhoneWindow类、DecorView类、ViewRootmpl类等
    • 主要包括:DecorView创建 & 显示

    3. 绘制流程概述

    • 从上可知,View的绘制流程开始于:ViewRootImpl对象的performTraversals()
    • 源码分析
    /**  * 源码分析:ViewRootImpl.performTraversals()  */  private void performTraversals() {  // 1. 执行measure流程        // 内部会调用performMeasure()        measureHierarchy(host, lp, res,desiredWindowWidth, desiredWindowHeight);        // 2. 执行layout流程        performLayout(lp, mWidth, mHeight);        // 3. 执行draw流程        performDraw();    }
    • 从上面的performTraversals()可知:View的绘制流程从顶级View(DecorView)的ViewGroup开始,一层一层从ViewGroup至子View遍历测绘

    即:自上而下遍历、由父视图到子视图、每一个 ViewGroup 负责测绘它所有的子视图,而最底层的 View 会负责测绘自身

    89a9d0fb83e0de856b311fea030254e2.png
    • 绘制的流程 = measure过程、layout过程、draw过程,具体如下
    15f3eb853ee2d78ea5d4d2c35e952f53.png
    e59bcaf4f22fb96f573c196f4c54e1d2.png

    下面,我将详细讲解View绘制的三大流程:measure过程、layout过程、draw过程


    4. 详细介绍

    4.1 Measure 过程

    • 作用 测量View的宽 / 高

    1、在某些情况下,需要多次测量(measure)才能确定View最终的宽/高;

    2、该情况下,measure过程后得到的宽 / 高可能不准确;

    3、此处建议:在layout过程中onLayout()去获取最终的宽 / 高

    • 具体流程
    8f3cc146715caba17097ae4cf2662c2d.png
    f71250bb60a4008df3d5d8fa7891efa1.png

    4.2 Layout过程

    • 作用 计算视图(View)的位置

    即计算View的四个顶点位置:Left、Top、Right 和 Bottom

    • 具体流程
    9d608aefd0d5385483a56fcf3edce68d.png
    2a0bcefa5786089ea88b8fb670e56ce8.png

    4.3 Draw过程

    • 作用 绘制View视图
    • 具体流程
    cde9b28d1c71a0140bb15243bca18bfe.png
    18d2aebbbe0d09835e36ad00a6b4816c.png

    5. 自定义View的步骤

    步骤1:实现Measure、Layout、Draw流程

    • 从View的工作流程(measure过程、layout过程、draw过程)来看,若要实现自定义View,根据自定义View的种类不同(单一View / ViewGroup),需自定义实现不同的方法
    • 主要是:onMeasure()、onLayout()、onDraw(),具体如下
    2f8cc13eeaaa2cbaa09620856d525ea9.png

    步骤2:自定义属性

    1. 在values目录下创建自定义属性的xml文件
    2. 在自定义View的构造方法中加载自定义XML文件 & 解析属性值
    3. 在布局文件中使用自定义属性

    各位看官们可以点赞转发,因为你们的赞同/鼓励是我写作的最大动力!

    展开全文
  • 与Neomorphic Design相关的Android库,它是一个自定义FrameLayout。 基本上,您只需将TextViews和Button放在此FrameLayout中,就可以了! Gradle依赖 将此添加到存储库末尾的root build.gradle文件中: ...
  • 自定义FrameLayout实现手势滑动控制各组件的分层显示
  • 嗨我正在开发Android应用程序,其中我使用一个自定义框架布局类。在那个班级里面,我正在使用一个drawable,并在画布的帮助下绘制它。我是按照以下方式做到的:public class BackgroundContainer extends ...

    嗨我正在开发Android应用程序,其中我使用一个自定义框架布局类。在那个班级里面,我正在使用一个drawable,并在画布的帮助下绘制它。我是按照以下方式做到的:

    public class BackgroundContainer extends FrameLayout implements OnTouchListener{

    Drawable mShadowedBackground;

    public BackgroundContainer(Context context) {

    super(context);

    init();

    }

    public BackgroundContainer(Context context, AttributeSet attrs) {

    super(context, attrs);

    init();

    }

    public BackgroundContainer(Context context, AttributeSet attrs, int defStyle) {

    super(context, attrs, defStyle);

    init();

    }

    private void init() {

    mShadowedBackground =

    getContext().getResources().getDrawable(R.drawable.actionbar);

    }

    @Override

    public boolean onTouchEvent(MotionEvent event)

    {

    Log.i("OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", "OOOOOOOOOOOOOOOOOOOOOO");

    switch(event.getAction())

    {

    case MotionEvent.ACTION_DOWN: {

    invalidate();

    }

    }

    return true;

    }

    @Override

    protected void onDraw(Canvas canvas) {

    mShadowedBackground.setBounds(getWidth()-150, 0, getWidth(), mOpenAreaHeight);

    canvas.save();

    canvas.translate(0, mOpenAreaTop);

    mShadowedBackground.draw(canvas);

    canvas.restore();

    }

    }

    }

    现在我想听点击甚至我的drawable。我实现ontouch事件,但它无法正常工作。我是以错误的方式做的。需要帮助,谢谢。

    展开全文
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼/** Copyright (C) 2016 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in ...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼

    /*

    * Copyright (C) 2016 The Android Open Source Project

    *

    * Licensed under the Apache License, Version 2.0 (the "License");

    * you may not use this file except in compliance with the License.

    * You may obtain a copy of the License at

    *

    * http://www.apache.org/licenses/LICENSE-2.0

    *

    * Unless required by applicable law or agreed to in writing, software

    * distributed under the License is distributed on an "AS IS" BASIS,

    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

    * See the License for the specific language governing permissions and

    * limitations under the License.

    */

    package com.android.systemui.recents.views;

    import android.content.Context;

    import android.graphics.Rect;

    import android.util.AttributeSet;

    import android.widget.FrameLayout;

    /**

    * This is an optimized FrameLayout whose layout is completely directed by its parent, and as a

    * result, does not propagate requestLayout() up the view hierarchy. Instead, it will

    * relayout its children with the last known layout bounds when a layout is requested from a child

    * view.

    */

    public class FixedSizeFrameLayout extends FrameLayout {

    private final Rect mLayoutBounds = new Rect();

    public FixedSizeFrameLayout(Context context) {

    super(context);

    }

    public FixedSizeFrameLayout(Context context, AttributeSet attrs) {

    super(context, attrs);

    }

    public FixedSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {

    super(context, attrs, defStyleAttr);

    }

    public FixedSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr,

    int defStyleRes) {

    super(context, attrs, defStyleAttr, defStyleRes);

    }

    @Override

    protected final void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    measureContents(MeasureSpec.getSize(widthMeasureSpec),

    MeasureSpec.getSize(heightMeasureSpec));

    }

    @Override

    protected final void onLayout(boolean changed, int left, int top, int right, int bottom) {

    mLayoutBounds.set(left, top, right, bottom);

    layoutContents(mLayoutBounds, changed);

    }

    @Override

    public final void requestLayout() {

    // The base ViewGroup constructor attempts to call requestLayout() before this class's

    // members are initialized so we should just propagate in that case

    if (mLayoutBounds == null || mLayoutBounds.isEmpty()) {

    super.requestLayout();

    } else {

    // If we are already laid out, then just reuse the same bounds to layout the children

    // (but not itself)

    // TODO: Investigate whether we should coalesce these to the next frame if needed

    measureContents(getMeasuredWidth(), getMeasuredHeight());

    layoutContents(mLayoutBounds, false);

    }

    }

    /**

    * Measures the contents of this fixed layout.

    */

    protected void measureContents(int width, int height) {

    super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),

    MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));

    }

    /**

    * Lays out the contents of this fixed layout.

    */

    protected void layoutContents(Rect bounds, boolean changed) {

    super.onLayout(changed, bounds.left, bounds.top, bounds.right, bounds.bottom);

    int width = getMeasuredWidth();

    int height = getMeasuredHeight();

    onSizeChanged(width, height, width, height);

    }

    }

    展开全文
  • 很好用的一款自定义android自定义FrameLayout布局Demo,可延伸到自定义View,有需要的小伙伴可以下载试下!
  • import android.widget.FrameLayout; public class TranslationView extends FrameLayout { private static final String TAG = "TranslationView"; private static final int DEFAULT_COLOR = 0x50000000; ...
    [img]http://dl2.iteye.com/upload/attachment/0123/0849/474be39c-263e-3347-97c3-7c32e9d53707.gif[/img]

    用法:

    translationView.show();
    translationView.hide();
    translationView.setShadowColor(ActivityCompat.getColor(MainActivity.this, R.color.blue));//设置背景颜色


    源码:

    import android.animation.Animator;
    import android.animation.AnimatorListenerAdapter;
    import android.animation.ObjectAnimator;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.support.annotation.ColorInt;
    import android.support.v4.view.MotionEventCompat;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.FrameLayout;

    public class TranslationView extends FrameLayout {

    private static final String TAG = "TranslationView";
    private static final int DEFAULT_COLOR = 0x50000000;
    private int mShadowColor = DEFAULT_COLOR;
    private boolean mIsShow = false;

    private View mTranslationView;


    public TranslationView(Context context) {
    super(context, null);
    }

    public TranslationView(Context context, AttributeSet attrs) {
    super(context, attrs, 0);
    }

    public TranslationView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    }


    @Override
    protected void onFinishInflate() {
    if (getChildCount() != 2) {
    throw new IllegalStateException("only and should contain two child view");
    }
    mTranslationView = getChildAt(1);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    Log.d(TAG, "onMeasure");
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    mTranslationView.layout(0, -mTranslationView.getHeight(), mTranslationView.getWidth(), 0);
    }


    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    if (mIsShow && child == mTranslationView) {
    canvas.drawColor(mShadowColor);
    }
    return super.drawChild(canvas, child, drawingTime);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    final int action = MotionEventCompat.getActionMasked(ev);
    switch (action){
    case MotionEvent.ACTION_DOWN:{
    if(mIsShow&&inShadow(ev)){
    hide();
    return true;
    }
    }
    }
    return super.onInterceptTouchEvent(ev);
    }

    private boolean inShadow(MotionEvent ev) {
    float x = ev.getX();
    float y = ev.getY();
    final float leftEdge = mTranslationView.getX();
    final float rightEdge = leftEdge+mTranslationView.getWidth();
    final float topEdge =mTranslationView.getHeight();
    final float bottomEdge = getHeight()+topEdge;
    return x > leftEdge && x < rightEdge && y > topEdge && y < bottomEdge;
    }


    public void show() {
    if (!mIsShow) {
    mIsShow = true;
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTranslationView, "translationY", mTranslationView.getTranslationY(), mTranslationView.getHeight());
    objectAnimator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationStart(Animator animation) {
    super.onAnimationStart(animation);
    invalidate();
    }
    });
    objectAnimator.start();
    }
    }

    public void hide() {
    if (mIsShow) {
    mIsShow = false;
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTranslationView, "translationY", mTranslationView.getTranslationY(), -mTranslationView.getHeight());
    objectAnimator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
    super.onAnimationEnd(animation);
    invalidate();
    }
    });
    objectAnimator.start();
    }
    }

    public void setShadowColor(@ColorInt int color) {
    mShadowColor = color;
    }
    }
    展开全文
  • Android自定义View设定到FrameLayout布局中实现多组件显示的方法 分享,需要的朋友可以参考一下
  • Android 自定义RecyclerView 实现真正的Gallery效果

    万次阅读 多人点赞 2014-07-27 17:45:07
    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38173061 ,本文出自:【张鸿洋的博客】上一篇博客我使用自定义HorizontalScrollView写了...详见:Android 自定义 HorizontalScrollView 打造再多
  • < ?xml version="1.0" encoding="utf-8"?... FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="fill_parent"android:layout_h...
  • Android - FrameLayout覆盖顺序

    万次阅读 2014-11-07 08:12:43
    FrameLayout覆盖顺序本文地址: http://blog.csdn.net/caroline_wendyFrameLayout: Child views are drawn in a stack, with the most recently added child on top.FrameLayout在一个栈(stack)内, 最先出现的在最...
  • < ?xml version="1.0" encoding="utf-8"?... FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="fill_parent"android:layout_h...
  • Android-FrameLayout(帧布局)

    千次阅读 2019-03-16 22:38:02
    FrameLayout(帧布局) 常用属性 android:foreground:*设置改帧布局容器的前景图像 android:foregroundGravity:设置前景图像显示的位置 1)最简单的例子 代码: &lt;TextView android:layout_width="200dp&...
  • 根据docs,FrameLayout中的子视图相互叠加,最近添加的视图位于顶部....值得一提的是,虽然我的自定义视图扩展了FrameLayout,但我确实给子视图充气,所以我的FrameLayout不是空的.xmlns:android="http://schemas.androi...
  • 如果想在自定义的View上面显示Button 等View组件需要完成如下任务 1.在自定义View的类中覆盖父类的构造(注意是2个参数的) public class MyView2 extends View{  public MyView2(Context context,AttributeSet ...
  • Android自定义照相机实现 近期小巫在学校有一个创新项目,也不是最近,是一个拖了很久的项目,之前一直没有去搞,最近因为要中期检查,搞得我跟小组成员一阵忙活,其实开发一款照相机软件并不太难,下面就是通过...
  • Android 自定义属性

    2016-03-16 15:41:02
    Android自定义属性 Android自定义view
  • 这篇博客为大家介绍Android自定义相机,并且实现倒计时拍照功能。 首先自定义拍照会用到SurfaceView控件显示照片的预览区域,以下是布局文件: activity_main.xml <FrameLayout xmlns:android=...
  • import android.widget.FrameLayout; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.WindowManager; /** * Created by ...
  • 主要介绍了Android自定义SeekBar滑动显示数字,使用FrameLayout结合SeekBar滑动时,数值显示,滑动停止时显示数字,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 本文讲述了Android自定义横向滑动菜单的实现。分享给大家供大家参考,具体如下: 前言  开发安卓过程中,经常会用到标题栏的样式,有时候传统方式不能满足开发者的需要,这时候就需要自定义控件来实现。(注意:...
  • 这是一篇与 Android 自定义控件相关的源码分析的文章. 主要从源码的角度分析自定义控件时, onMeasure() 方法处理其宽或高在布局文件中被设置为 wrap_content 时的必要性.
  • 自定义圆形FrameLayout布局

    千次阅读 2017-06-15 15:59:05
    import android.widget.FrameLayout; import com.wxl.circleimageviewdemo.R; /** * 圆形FrameLayout,可以设置外圆圈 */ public class CircleFrameLayout extends FrameLayout{ private Paint roundPaint; ...
  • 下面一段代码给大家介绍了android 自定义顶部导航栏控件功能,具体代码如下所示: class HeaderBar @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ...
  • Android自定义相机实现定时拍照

    千次阅读 2016-12-28 20:02:12
    这篇博客为大家介绍Android自定义相机,并且实现倒计时拍照功能。首先自定义拍照会用到SurfaceView控件显示照片的预览区域,以下是布局文件:activity_main.xml<FrameLayout xmlns:android=...
  • 本文实例为大家分享了Android自定义View实现跟随手指移动的小兔子,供大家参考,具体内容如下 自定义的View实现跟随手指的小兔子 按前面的例子新创建一个project,再在project中新创建一个module 将需要的背景图和...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,461
精华内容 10,184
关键字:

android自定义framelayout