欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android自定义RadioGroup可以添加多种布局的实现方法

程序员文章站 2023-01-01 23:39:33
android自带的radiogroup是继承自linearlayout,如果布局的时候不是直接写radiobutton,即radiobutton外面还包了一层容器,这时分...

android自带的radiogroup是继承自linearlayout,如果布局的时候不是直接写radiobutton,即radiobutton外面还包了一层容器,这时分组是不成功的,因为查找不到radiobutton,如果要实现这种效果呢,于是看了radiogroup的源码,发现问题在于addview方法和自定义的passthroughhierarchychangelistener;

下面就这两个地方动手脚,先拷贝源码,然后去掉radiogroup(context context, attributeset attrs) 中的方法,我新增了一个方法,查找viewgroup控件中的radiobutton

复制代码 代码如下:

/** 查找radiobutton控件 */
    public radiobutton findradiobutton(viewgroup group) {
        radiobutton resbtn = null;
        int len = group.getchildcount();
        for (int i = 0; i < len; i++) {
            if (group.getchildat(i) instanceof radiobutton) {
                resbtn = (radiobutton) group.getchildat(i);
            } else if (group.getchildat(i) instanceof viewgroup) {
                findradiobutton((viewgroup) group.getchildat(i));
            }
        }
        return resbtn;
    }

addview的实现如下:

复制代码 代码如下:

@override
    public void addview(view child, int index, viewgroup.layoutparams params) {
        if (child instanceof radiobutton) {
            final radiobutton button = (radiobutton) child;
            if (button.ischecked()) {
                mprotectfromcheckedchange = true;
                if (mcheckedid != -1) {
                    setcheckedstateforview(mcheckedid, false);
                }
                mprotectfromcheckedchange = false;
                setcheckedid(button.getid());
            }
        } else if (child instanceof viewgroup) {  //这里是我添加的代码
            final radiobutton button = findradiobutton((viewgroup) child);
            if (button.ischecked()) {
                mprotectfromcheckedchange = true;
                if (mcheckedid != -1) {
                    setcheckedstateforview(mcheckedid, false);
                }
                mprotectfromcheckedchange = false;
                setcheckedid(button.getid());
            }
        }

        super.addview(child, index, params);
    }


然后在自定义的passthroughhierarchychangelistener中修改:
复制代码 代码如下:

private class passthroughhierarchychangelistener implements
            viewgroup.onhierarchychangelistener {
        private viewgroup.onhierarchychangelistener monhierarchychangelistener;

        public void onchildviewadded(view parent, view child) {
            if (parent == myradiogroup.this && child instanceof radiobutton) {
                int id = child.getid();
                // generates an id if it's missing
                if (id == view.no_id) {
                    id = child.hashcode();
                    child.setid(id);
                }
                ((radiobutton) child)
                        .setoncheckedchangelistener(mchildoncheckedchangelistener);
            } else if (parent == myradiogroup.this   //这里是我添加的代码
                    && child instanceof viewgroup) {
                radiobutton btn = findradiobutton((viewgroup) child);
                int id = btn.getid();
                // generates an id if it's missing
                if (id == view.no_id) {
                    id = btn.hashcode();
                    btn.setid(id);
                }
                btn.setoncheckedchangelistener(mchildoncheckedchangelistener);
            }

            if (monhierarchychangelistener != null) {
                monhierarchychangelistener.onchildviewadded(parent, child);
            }
        }

        public void onchildviewremoved(view parent, view child) {
            if (parent == myradiogroup.this && child instanceof radiobutton) {
                ((radiobutton) child).setoncheckedchangelistener(null);
            } else if (parent == myradiogroup.this
                    && child instanceof viewgroup) {
                findradiobutton((viewgroup) child).setoncheckedchangelistener(
                        null);
            }
            if (monhierarchychangelistener != null) {
                monhierarchychangelistener.onchildviewremoved(parent, child);
            }
        }
    }


做好了上面的步骤,下面布局就可以按照自己的意思来了,并且分组效果也不会影响

下面我的布局文件demo:

复制代码 代码如下:

<com.huaheng.wy.view.myradiogroup
        android:id="@+id/rg"
        android:layout_width="fill_parent"
        android:layout_height="65dp"
        android:layout_alignparentbottom="true"
        android:background="@drawable/yst_i_bottom"
        android:orientation="horizontal" >

        <!-- 首页 -->

        <framelayout
            android:id="@+id/left"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginbottom="2dp"
            android:layout_margintop="3dp"
            android:layout_weight="1" >

            <radiobutton
                android:id="@+id/rbtn_home"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:background="@null"
                android:button="@null"
                android:drawabletop="@drawable/yst_i_bottomhome_selector"
                android:gravity="center_horizontal"
                android:text="首页"
                android:textcolor="@color/white"
                android:textsize="@dimen/font_5" />

            <button
                android:id="@+id/btn_home_point"
                android:layout_width="21dp"
                android:layout_height="20dp"
                android:layout_gravity="top|right"
                android:layout_marginright="10dp"
                android:layout_margintop="0dp"
                android:background="@drawable/notice_bg"
                android:text="1"
                android:textcolor="@color/white"
                android:textsize="@dimen/font_6"
                android:visibility="gone" />
        </framelayout>
        <!-- 历史 -->

        <radiobutton
            android:id="@+id/rbtn_his"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginbottom="2dp"
            android:layout_margintop="3dp"
            android:layout_weight="1"
            android:background="@null"
            android:button="@null"
            android:drawabletop="@drawable/yst_i_bottomhis_selector"
            android:gravity="center_horizontal"
            android:text="历史"
            android:textcolor="@color/white"
            android:textsize="@dimen/font_5" />

        <!-- 扫描 -->

        <button
            android:id="@+id/rbtn_scan"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:background="@drawable/scan_selector"
            android:button="@null"
            android:gravity="center_horizontal|bottom"
            android:paddingbottom="7dp"
            android:text="扫描"
            android:textcolor="@color/white"
            android:textsize="@dimen/font_5" />

        <radiobutton
            android:id="@+id/rbtn_seek"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginbottom="2dp"
            android:layout_margintop="3dp"
            android:layout_weight="1"
            android:background="@null"
            android:button="@null"
            android:drawabletop="@drawable/yst_i_bottomseek_selector"
            android:gravity="center_horizontal"
            android:text="搜索"
            android:textcolor="@color/white"
            android:textsize="@dimen/font_5" />

        <radiobutton
            android:id="@+id/rbtn_more"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginbottom="2dp"
            android:layout_margintop="3dp"
            android:layout_weight="1"
            android:background="@null"
            android:button="@null"
            android:drawabletop="@drawable/yst_i_bottommore_selector"
            android:gravity="center_horizontal"
            android:text="更多"
            android:textcolor="@color/white"
            android:textsize="@dimen/font_5" />
    </com.huaheng.wy.view.myradiogroup>


所有实现代码都在这里,下面我就把自定义的radiogroup的代码,完整的贴出来
复制代码 代码如下:

/*
 * copyright (c) 2006 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.huaheng.wy.view;

import android.content.context;
import android.content.res.typedarray;
import android.util.attributeset;
import android.view.view;
import android.view.viewgroup;
import android.widget.compoundbutton;
import android.widget.linearlayout;
import android.widget.radiobutton;

public class myradiogroup extends linearlayout {
    // holds the checked id; the selection is empty by default
    private int mcheckedid = -1;
    // tracks children radio buttons checked state
    private compoundbutton.oncheckedchangelistener mchildoncheckedchangelistener;
    // when true, moncheckedchangelistener discards events
    private boolean mprotectfromcheckedchange = false;
    private oncheckedchangelistener moncheckedchangelistener;
    private passthroughhierarchychangelistener mpassthroughlistener;

    public myradiogroup(context context) {
        super(context);
        setorientation(vertical);
        init();
    }

    public myradiogroup(context context, attributeset attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mchildoncheckedchangelistener = new checkedstatetracker();
        mpassthroughlistener = new passthroughhierarchychangelistener();
        super.setonhierarchychangelistener(mpassthroughlistener);
    }

    @override
    public void setonhierarchychangelistener(onhierarchychangelistener listener) {
        // the user listener is delegated to our pass-through listener
        mpassthroughlistener.monhierarchychangelistener = listener;
    }

    @override
    protected void onfinishinflate() {
        super.onfinishinflate();

        // checks the appropriate radio button as requested in the xml file
        if (mcheckedid != -1) {
            mprotectfromcheckedchange = true;
            setcheckedstateforview(mcheckedid, true);
            mprotectfromcheckedchange = false;
            setcheckedid(mcheckedid);
        }
    }

    @override
    public void addview(view child, int index, viewgroup.layoutparams params) {
        if (child instanceof radiobutton) {
            final radiobutton button = (radiobutton) child;
            if (button.ischecked()) {
                mprotectfromcheckedchange = true;
                if (mcheckedid != -1) {
                    setcheckedstateforview(mcheckedid, false);
                }
                mprotectfromcheckedchange = false;
                setcheckedid(button.getid());
            }
        } else if (child instanceof viewgroup) {
            final radiobutton button = findradiobutton((viewgroup) child);
            if (button.ischecked()) {
                mprotectfromcheckedchange = true;
                if (mcheckedid != -1) {
                    setcheckedstateforview(mcheckedid, false);
                }
                mprotectfromcheckedchange = false;
                setcheckedid(button.getid());
            }
        }

        super.addview(child, index, params);
    }

    /** 查找radiobutton控件 */
    public radiobutton findradiobutton(viewgroup group) {
        radiobutton resbtn = null;
        int len = group.getchildcount();
        for (int i = 0; i < len; i++) {
            if (group.getchildat(i) instanceof radiobutton) {
                resbtn = (radiobutton) group.getchildat(i);
            } else if (group.getchildat(i) instanceof viewgroup) {
                findradiobutton((viewgroup) group.getchildat(i));
            }
        }
        return resbtn;
    }

    /**
     * <p>
     * sets the selection to the radio button whose identifier is passed in
     * parameter. using -1 as the selection identifier clears the selection;
     * such an operation is equivalent to invoking {@link #clearcheck()}.
     * </p>
     *
     * @param id
     *            the unique id of the radio button to select in this group
     *
     * @see #getcheckedradiobuttonid()
     * @see #clearcheck()
     */
    public void check(int id) {
        // don't even bother
        if (id != -1 && (id == mcheckedid)) {
            return;
        }

        if (mcheckedid != -1) {
            setcheckedstateforview(mcheckedid, false);
        }

        if (id != -1) {
            setcheckedstateforview(id, true);
        }

        setcheckedid(id);
    }

    private void setcheckedid(int id) {
        mcheckedid = id;
        if (moncheckedchangelistener != null) {
            moncheckedchangelistener.oncheckedchanged(this, mcheckedid);
        }
    }

    private void setcheckedstateforview(int viewid, boolean checked) {
        view checkedview = findviewbyid(viewid);
        if (checkedview != null && checkedview instanceof radiobutton) {
            ((radiobutton) checkedview).setchecked(checked);
        }
    }

    /**
     * <p>
     * returns the identifier of the selected radio button in this group. upon
     * empty selection, the returned value is -1.
     * </p>
     *
     * @return the unique id of the selected radio button in this group
     *
     * @see #check(int)
     * @see #clearcheck()
     */
    public int getcheckedradiobuttonid() {
        return mcheckedid;
    }

    /**
     * <p>
     * clears the selection. when the selection is cleared, no radio button in
     * this group is selected and {@link #getcheckedradiobuttonid()} returns
     * null.
     * </p>
     *
     * @see #check(int)
     * @see #getcheckedradiobuttonid()
     */
    public void clearcheck() {
        check(-1);
    }

    /**
     * <p>
     * register a callback to be invoked when the checked radio button changes
     * in this group.
     * </p>
     *
     * @param listener
     *            the callback to call on checked state change
     */
    public void setoncheckedchangelistener(oncheckedchangelistener listener) {
        moncheckedchangelistener = listener;
    }

    /**
     * {@inheritdoc}
     */
    @override
    public layoutparams generatelayoutparams(attributeset attrs) {
        return new myradiogroup.layoutparams(getcontext(), attrs);
    }

    /**
     * {@inheritdoc}
     */
    @override
    protected boolean checklayoutparams(viewgroup.layoutparams p) {
        return p instanceof myradiogroup.layoutparams;
    }

    @override
    protected linearlayout.layoutparams generatedefaultlayoutparams() {
        return new layoutparams(layoutparams.wrap_content,
                layoutparams.wrap_content);
    }

    /**
     * <p>
     * this set of layout parameters defaults the width and the height of the
     * children to {@link #wrap_content} when they are not specified in the xml
     * file. otherwise, this class ussed the value read from the xml file.
     * </p>
     *
     * <p>
     * see {@link android.r.styleable#linearlayout_layout linearlayout
     * attributes} for a list of all child view attributes that this class
     * supports.
     * </p>
     *
     */
    public static class layoutparams extends linearlayout.layoutparams {
        /**
         * {@inheritdoc}
         */
        public layoutparams(context c, attributeset attrs) {
            super(c, attrs);
        }

        /**
         * {@inheritdoc}
         */
        public layoutparams(int w, int h) {
            super(w, h);
        }

        /**
         * {@inheritdoc}
         */
        public layoutparams(int w, int h, float initweight) {
            super(w, h, initweight);
        }

        /**
         * {@inheritdoc}
         */
        public layoutparams(viewgroup.layoutparams p) {
            super(p);
        }

        /**
         * {@inheritdoc}
         */
        public layoutparams(marginlayoutparams source) {
            super(source);
        }

        /**
         * <p>
         * fixes the child's width to
         * {@link android.view.viewgroup.layoutparams#wrap_content} and the
         * child's height to
         * {@link android.view.viewgroup.layoutparams#wrap_content} when not
         * specified in the xml file.
         * </p>
         *
         * @param a
         *            the styled attributes set
         * @param widthattr
         *            the width attribute to fetch
         * @param heightattr
         *            the height attribute to fetch
         */
        @override
        protected void setbaseattributes(typedarray a, int widthattr,
                int heightattr) {

            if (a.hasvalue(widthattr)) {
                width = a.getlayoutdimension(widthattr, "layout_width");
            } else {
                width = wrap_content;
            }

            if (a.hasvalue(heightattr)) {
                height = a.getlayoutdimension(heightattr, "layout_height");
            } else {
                height = wrap_content;
            }
        }
    }

    /**
     * <p>
     * interface definition for a callback to be invoked when the checked radio
     * button changed in this group.
     * </p>
     */
    public interface oncheckedchangelistener {
        /**
         * <p>
         * called when the checked radio button has changed. when the selection
         * is cleared, checkedid is -1.
         * </p>
         *
         * @param group
         *            the group in which the checked radio button has changed
         * @param checkedid
         *            the unique identifier of the newly checked radio button
         */
        public void oncheckedchanged(myradiogroup group, int checkedid);
    }

    private class checkedstatetracker implements
            compoundbutton.oncheckedchangelistener {
        public void oncheckedchanged(compoundbutton buttonview,
                boolean ischecked) {
            // prevents from infinite recursion
            if (mprotectfromcheckedchange) {
                return;
            }

            mprotectfromcheckedchange = true;
            if (mcheckedid != -1) {
                setcheckedstateforview(mcheckedid, false);
            }
            mprotectfromcheckedchange = false;

            int id = buttonview.getid();
            setcheckedid(id);
        }
    }

    /**
     * <p>
     * a pass-through listener acts upon the events and dispatches them to
     * another listener. this allows the table layout to set its own internal
     * hierarchy change listener without preventing the user to setup his.
     * </p>
     */
    private class passthroughhierarchychangelistener implements
            viewgroup.onhierarchychangelistener {
        private viewgroup.onhierarchychangelistener monhierarchychangelistener;

        public void onchildviewadded(view parent, view child) {
            if (parent == myradiogroup.this && child instanceof radiobutton) {
                int id = child.getid();
                // generates an id if it's missing
                if (id == view.no_id) {
                    id = child.hashcode();
                    child.setid(id);
                }
                ((radiobutton) child)
                        .setoncheckedchangelistener(mchildoncheckedchangelistener);
            } else if (parent == myradiogroup.this
                    && child instanceof viewgroup) {
                radiobutton btn = findradiobutton((viewgroup) child);
                int id = btn.getid();
                // generates an id if it's missing
                if (id == view.no_id) {
                    id = btn.hashcode();
                    btn.setid(id);
                }
                btn.setoncheckedchangelistener(mchildoncheckedchangelistener);
            }

            if (monhierarchychangelistener != null) {
                monhierarchychangelistener.onchildviewadded(parent, child);
            }
        }

        public void onchildviewremoved(view parent, view child) {
            if (parent == myradiogroup.this && child instanceof radiobutton) {
                ((radiobutton) child).setoncheckedchangelistener(null);
            } else if (parent == myradiogroup.this
                    && child instanceof viewgroup) {
                findradiobutton((viewgroup) child).setoncheckedchangelistener(
                        null);
            }
            if (monhierarchychangelistener != null) {
                monhierarchychangelistener.onchildviewremoved(parent, child);
            }
        }
    }
}