zoukankan      html  css  js  c++  java
  • 如何定义自己的ViewGroup

    在发展中,有时会遇到一些要求。布局和控制系统不仅提供使用,以满足我们的发展,所以这一次就行,通常是你自己的自定义布局(ViewGroup)并控制(View)该。我在这里,我们将用一个简单的例子,当他们解释他们的定义ViewGroup基本流程,我希望能帮助朋友不理解这个过程。

    首先,我们想要实现的布局图例如以下:


    就这样看起来十分简单,用系统提供的布局就能够实现了这个效果,根本不须要自己定义ViewGroup来实现嘛!!

    。只是,假设你自己去尝试一下用系统的布局来做。你就会发现一些问题了。问题1是:要实现各个view错开的效果,就必须为他们设置不同的固定外边距參数。这样可能带来的问题就是:不同手机。显示效果可能会不一样。也就是适配问题!问题2是:假设想要增加的View多了,还须要自己计算每一个View的外边距參数。非常坑爹,再说,这里主要是解说自己定义ViewGroup的基本流程,所以,样例越简单,也就越好理解了!

    首先,我们能够自己定义自己ViewGroup想要的属性;这里我为ViewGroup定义了两个属性,horizontal_spacing(布局中的view的水平间距)和vertical_spacing(布局中的View的垂直间距);另外,还定义了一个布局參数的属性,layout_vertical_spacing。这个属性能够供我们自己定义的ViewGroup中的View使用。

    attrs.xml 的内容:

    <?xml version="1.0" encoding="utf-8"?

    > <resources> <declare-styleable name="MyCustomLayout"> <attr name="horizontal_spacing" format="dimension" /> <attr name="vertical_spacing" format="dimension" /> </declare-styleable> <declare-styleable name="MyCustomLayout_LayoutParams"> <attr name="layout_vertical_spacing" format="dimension" /> </declare-styleable> </resources>

    在dimens.xml中定义两个属性的默认值。

    dimens.xml 的内容:

    <?

    xml version="1.0" encoding="utf-8"?> <resources> <dimen name="horizontal_spacing">10dp</dimen> <dimen name="vertical_spacing">10dp</dimen> </resources>

    接着,最基本的步骤来了。先看看我们自己定义的ViewGroup。

    以下是源代码:

    package com.customlayout.mycustomlayout;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.util.AttributeSet;
    import android.view.View;
    import android.view.ViewGroup;
    
    
    public class MyCustomLayout extends ViewGroup {
    
        private int mHorizontalSpacing;
        private int mVerticalSpacing;
    
    
        public MyCustomLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyCustomLayout);
            mHorizontalSpacing = ta.getDimensionPixelSize(R.styleable.MyCustomLayout_horizontal_spacing,
                    getResources().getDimensionPixelSize(R.dimen.horizontal_spacing));
            mVerticalSpacing = ta.getDimensionPixelSize(R.styleable.MyCustomLayout_vertical_spacing,
                    getResources().getDimensionPixelSize(R.dimen.vertical_spacing));
            ta.recycle();
        }
    
        @Override
        protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
            // TODO Auto-generated method stub
            return p != null;
        }
    
    
        @Override
        protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
            // TODO Auto-generated method stub
            return (LayoutParams) p;
        }
    
    
        @Override
        public LayoutParams generateLayoutParams(AttributeSet attrs) {
            return new LayoutParams(getContext(), attrs);
        }
    
    
        @Override
        protected LayoutParams generateDefaultLayoutParams() {
            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int width = 0;
            int height = getPaddingTop();
            int verticalSpacing;
            final int count = getChildCount();
            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
                verticalSpacing = mVerticalSpacing;
                LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (lp.verticalSpacing > 0) {
                    verticalSpacing += lp.verticalSpacing;
                }
                width = getPaddingLeft() + mHorizontalSpacing * i;
                lp.x = width;
                lp.y = height;
                width += child.getMeasuredWidth();
                height += verticalSpacing;
            }
            width += getPaddingRight();
            height += getChildAt(getChildCount() - 1).getMeasuredHeight() + getPaddingBottom();
    
            setMeasuredDimension(resolveSize(width, widthMeasureSpec),
                    resolveSize(height, heightMeasureSpec));
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            final int count = getChildCount();
            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                LayoutParams lp = (LayoutParams) child.getLayoutParams();
                child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
            }
        }
    
        public static class LayoutParams extends ViewGroup.LayoutParams {
            public int x;
            public int y;
            public int verticalSpacing;
    
            public LayoutParams(Context c, AttributeSet attrs) {
                super(c, attrs);
                TypedArray ta = c.obtainStyledAttributes(attrs, R.styleable.MyCustomLayout_LayoutParams);
                verticalSpacing = ta.getDimensionPixelSize(R.styleable.MyCustomLayout_LayoutParams_layout_vertical_spacing, -1);
                ta.recycle();
            }
    
            public LayoutParams(int w, int h) {
                super(w, h);
            }
        }
    }
    
    首先,我们从MyCustomLayout.java 的构造方法说起。

    public MyCustomLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyCustomLayout);
            mHorizontalSpacing = ta.getDimensionPixelSize(R.styleable.MyCustomLayout_horizontal_spacing,
                    getResources().getDimensionPixelSize(R.dimen.horizontal_spacing));
            mVerticalSpacing = ta.getDimensionPixelSize(R.styleable.MyCustomLayout_vertical_spacing,
                    getResources().getDimensionPixelSize(R.dimen.vertical_spacing));
            ta.recycle();
        }
    这个构造方法有两个參数,当中attrs是我们布局的属性集合,有了这个參数。我们就能够获取到在布局文件xml中设置的相关属性了。

    接着,讲onMeasure方法。顾名思义。这种方法是一个測量方法。它的作用是:遍历布局中的每个View。对每个View进行測量(调用measureChild方法),接着再为View设置LayoutParams。设置View的位置,即在屏幕上的x。y坐标。以便供onLayout方法中使用。这里的LayoutParams是我们重写的,当中int x和int y保存了布局中View的坐标位置。注意:重写LayoutParams类时。必需要对ViewGroup中的checkLayoutParams(ViewGroup.LayoutParams p)、generateLayoutParams(ViewGroup.LayoutParams p)、generateLayoutParams(AttributeSet attrs)、generateDefaultLayoutParams()进行重写,否则将会出现异常。。。

    接下来。讲onLayout方法;这种方法是对布局中的所有View进行位置部署。从方法体中能够看到,它通过一个遍历,对布局中的每个View调用layout方法进行位置部署。


    好了,在这里略微总结下:自己定义ViewGroup流程中。 最主要是对onMeasure和onLayout两个方法进行重写。onMeasure通过遍历布局中的View。为每个View測量了大小、计算布局參数等。onLayout则是通过遍历布局中的View,为每个View进行位置布置。

    在activity_main.xml 中使用我们自己定义的ViewGroup:

    <com.customlayout.mycustomlayout.MyCustomLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:customLayout="http://schemas.android.com/apk/res-auto"
        android:id="@+id/layout"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@android:color/white"
        customLayout:horizontal_spacing="65dp"
        customLayout:vertical_spacing="85dp">
    
        <View
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:background="#00ff00" />
    
        <View
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:background="#0000ff" />
    
        <View
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:background="#ff0000" />
    
        <View
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:background="#0ff000" />
    
        <View
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:background="#00f0f0" />
    
    </com.customlayout.mycustomlayout.MyCustomLayout>


    MainActivity.java 的源代码:

    package com.customlayout.mycustomlayout;
    
    import android.app.Activity;
    import android.graphics.RectF;
    import android.os.Bundle;
    import android.view.animation.AlphaAnimation;
    import android.view.animation.Animation;
    import android.view.animation.AnimationSet;
    import android.view.animation.BounceInterpolator;
    import android.view.animation.LayoutAnimationController;
    import android.view.animation.TranslateAnimation;
    
    public class MainActivity extends Activity {
    
        MyCustomLayout layout;
        LayoutAnimationController layoutAnimationController;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            layout = (MyCustomLayout) findViewById(R.id.layout);
    
            AnimationSet set = new AnimationSet(true);
    
            AlphaAnimation a = new AlphaAnimation(0f, 1f);
            a.setDuration(500);
            TranslateAnimation t = new TranslateAnimation(Animation.RELATIVE_TO_SELF, -1f,
                    Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f);
            t.setDuration(500);
    
            set.addAnimation(t);
            set.addAnimation(a);
            layoutAnimationController = new LayoutAnimationController(set);
            layout.setLayoutAnimation(layoutAnimationController);
    
        }
    }
    

    最后。讲一下布局动画。在ViewGroup类中,有一个属性是LayoutAnimation,也就是所谓的布局动画。仅仅要我们指定一个动画给这个属性,那么ViewGroup中的每个在布局时都可以带有动画效果。在上面的onCreate()方法中。我指定了一个动画集合并设置给了我自己定义好的MyCustomLayout。

    执行一下程序,在网上看到。MyCustomLayout每View将在顺序和我的电影了,现在被指定MyCustomLayout在,其效果是非常酷!

    结合这个简单的例子,给你自己定义的简单介绍ViewGroup方法;我们使用其他复杂的布局,所有使用我上面描述将实施的方法。他们的计算将仅比许多上述例子更复杂,但基本原理仍然是。

  • 相关阅读:
    Python
    Kubernetes之二workload资源编排
    Kubernetes之一安装
    DockerFile
    Docker的安装和使用
    Elastic Stack配置和使用
    虚拟化KVM应用
    Tomcat安装和使用
    Keepalived实现双主模型的ipvs高可用集群+实现双主模型的nginx高可用集群
    实验lvs+dns+nfs+mysql+web
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5019086.html
Copyright © 2011-2022 走看看