zoukankan      html  css  js  c++  java
  • android应用开发--------------看RadioGroup源代码,写相似单选选项卡的集成控件(如底部导航,tab等等)

    博客为 有时个哥 原创。如需转载请标明出处:http://blog.csdn.net/ls703/article/details/46694967

    上面就是需求设计,4个类似的布局控件。每次仅仅能选择一个。然后得到上面相应的钱数。(上面仅仅是效果图,实际数据是从server获取,然后付到控件上)

    看到这样的。我们就回忆到,几种实现方法。

    1.把这个总体写一个布局。在xml布局中。复制粘贴,代码。凑够4个。很不建议这样。由于4个的布局样式是一样的,仅仅是数据可能不同样,所以我们应该写一个组合控件然后反复利用。

    2.就是写一个单个的布局控件,然后设置一些设置数据的方法,然后反复利用它。引入四个控件。这样在一定程度上达到了代码反复利用性,可是以后在布局中这一块要是去掉。可能你要改动的代码也是会比較多了。

    3.另一种就是使用GridView。我们看多多个同样的布局。就会相到使用listView和GridView。

    这样的方法是能够使用的,只是在有个问题是,假设没有数据的前提下,它将会是一个空布局。不太好看。对于后期改动或去掉这一部分布局的话,相对来说好算比較好改动,去电一个GridView和他相相应的adapter就好了。

    以下我们在针对另外一种来说一下,解决一下他的不好改动的特点。由于。我们一般写完程序之后就会懒得改动,所以我们在第一次编写的时候,要为以后的改动做好准备,一切为了方便改动。由于程序猿都知道,产品的需求是一直在变化。。。。。。你们懂就好,假如。产品想把这个东西换一个样式,或是直接其用掉这一部分布局。那我们就尽量做到,删除几行代码搞定它。

    那么就须要我们在2方式的基础上再封装一层。

    把四个弄成一个控件,然后再总体加进去。

    你们细致看,上面的是不是一个单选按钮组?就和androd的radiobutton控件相似,然后用一个radioGroup包括。

    所以我们能够针对这个写一个类似RadioGroup和RadioButton那样类似的东西。

    首先先解决RadioButton类似的控件。先写一个布局,这种布局。

    布局为,

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical" >
    
        <LinearLayout
            android:id="@+id/llbg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_marginTop="16dp"
            android:background="@drawable/btn_recharge"
            android:orientation="vertical" >
    
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="right"
                android:layout_marginRight="5dp"
                android:orientation="horizontal" >
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="¥"
                    android:textColor="@color/white" />
    
                <TextView
                    android:id="@+id/tvMoney"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="0"
                    android:textColor="@color/white"
                    android:textSize="18sp" />
            </LinearLayout>
    
            <TextView
                android:id="@+id/tvcords"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="right"
                android:layout_marginRight="5dp"
                android:text="0币"
                android:textColor="@color/yellow"
                android:textSize="12sp" />
        </LinearLayout>
    
        <TextView
            android:id="@+id/tvZengs"
            android:visibility="visible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:background="@drawable/icon_recharge_discount"
            android:gravity="center_horizontal"
            android:text="送0"
            android:textColor="@color/white"
            android:textSize="10sp" />
    
    </RelativeLayout>


    就这么简单写一下。

    然后把其总体封装为一个组合控件。编写方法什么的。

    public class RechargeValueLayout extends FrameLayout{
    
    	private View view;
    	private TextView mZengs;
    	private TextView mMoney;
    	private TextView mCords;
    	private String money;
    	private LinearLayout mllbg;
    
    	private Boolean isChecked;
    	
    	public RechargeValueLayout(Context context, AttributeSet attrs, int defStyle) {
    		super(context, attrs, defStyle);
    		// TODO Auto-generated constructor stub
    	}
    
    	public RechargeValueLayout(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		view = View.inflate(context, R.layout.recharge_value_item, this);
    		mllbg = (LinearLayout)view.findViewById(R.id.llbg);
    		mZengs = (TextView)view.findViewById(R.id.tvZengs);
    		mMoney = (TextView)view.findViewById(R.id.tvMoney);
    		mCords = (TextView)view.findViewById(R.id.tvcords);
    	}
    
    	public RechargeValueLayout(Context context) {
    		super(context);
    	}
    	
    	public void setChecked(Boolean isChecked){
    		Log.i("TAG2", "isSelected===>>>>"+isChecked);
    		if(isChecked==this.isChecked){
    			return;
    		}
    		if(isChecked){
    			mllbg.setBackgroundResource(R.drawable.btn_recharge_active);
    		}else{
    			mllbg.setBackgroundResource(R.drawable.btn_recharge);
    		}
    		this.isChecked = isChecked;
    	}
    	
    	public boolean isChecked(){
    		return this.isChecked;
    	}
    	
    	public void setZengsValue(int value){
    		if(value>0){
    			mZengs.setVisibility(View.VISIBLE);
    			mZengs.setText("送"+value+"");
    		}else{
    			mZengs.setVisibility(View.GONE);
    		}
    	}
    	public String getMoneyValue(){
    		return this.money;
    	}
    	public void setMoneyValue(int value){
    		if(value>0){
    			mMoney.setText(value+"");
    			this.money = value+"";
    		}else{
    			mMoney.setText("0币");
    		}
    	}
    	
    	public void setCordsValue(int value){
    		if(value>0){
    			mCords.setText(value+"币");
    		}else{
    			mCords.setText("0币");
    		}
    	}
    	
    	
    }

    这样,你就直接能够引用其路径,直接使用这个组合控件。事实上到这里。就实现了2方式所说的。

    可是仅用这个,那么我们仅仅是实现了布局重用。可是不好进行替换改动,你能够想想,这么一个控件里有3个textView。而且我们要对其textView进行赋值。也就是说我们要对12TextView经行操作,假设这12TextView放在我们activity里,一是代码过于臃肿。二是你想想,假设以后去掉的话,我们就得对这全部涉及到这12个TextView的代码都得进行删除。

    所以我们如今要把这四个控件再给他们一个类似RadioGroup的容器,在RadioGroup里有一个点击事件,仅仅要对RadioCroup设置上监听,那么在RadioGroup的控件就自己主动设置上了监听,所以,咱activity里面我们仅仅须要这个RadioGroup的控件代码。1个对12个。你们想想到知道兴许改动操作那种方便。

    開始是想的定义的布局控件,这个布局控件得到自己里面的孩子数。然后再遍历循环给孩子设置上监听。但是失败了。

    详细的说说失败的代码。

      <com.song.components.RechargeValueGroup
                android:id="@+id/rechargeGroup"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" >
    
                <com.song.components.RechargeValueLayout
                    android:id="@+id/tab1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1" >
                </com.song.components.RechargeValueLayout>
    
                <com.song.components.RechargeValueLayout
                    android:id="@+id/tab2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1" >
                </com.song.components.RechargeValueLayout>
    
                <com.song.components.RechargeValueLayout
                    android:id="@+id/tab3"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1" >
                </com.song.components.RechargeValueLayout>
                <!--
                <com.song.components.RechargeValueLayout
                     android:id="@+id/tab4"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1" >
                </com.song.components.RechargeValueLayout>
                -->
            </com.song.components.RechargeValueGroup>


    这是布局,首先把RechargeValueLayout放到RechargeValueGroup里面,然后RechargeValueGroup里面得到自己的孩子数,

    public class RechargeValueGroup extends FrameLayout {
    
    	private OnItemTabClickListener listener;
    	
    	public RechargeValueGroup(Context context, AttributeSet attrs, int defStyle) {
    		super(context, attrs, defStyle);
    		// TODO Auto-generated constructor stub
    	}
    
    	public RechargeValueGroup(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		init();
    	}
    
    	
    	public RechargeValueGroup(Context context) {
    		super(context);
    		// TODO Auto-generated constructor stub
    	}
    	
    	private void init() {
    		int count = getChildCount();
    		for (int i = 0; i < count; i++) {
    			getChildAt(i).setOnClickListener(new OnClickListener() {
    				
    				@Override
    				public void onClick(View v) {
    					if(listener!=null){
    						listener.onItemClick(RechargeValueGroup.this,v.getId());
    					}
    				}
    			});
    		}
    	}
    
    	/**
    	 * 定义Group的监听,点击每个子button就会返回相相应子button的信息,以方便操作
    	 */
    	public interface OnItemTabClickListener {
    		public void onItemClick(RechargeValueGroup view, int checkedId);
    	}
    	
    }


    但是这样有个问题。在构造函数运行的时候是获得不到孩子数的,这样就没法给孩子设置上监听了。

    然后我就想记得曾经是在onMeasure里面能够得到孩子数。把init的部分放到onMeasure或onLayout方法里,孩子数是得到了。但是这两个方法是,仅仅要布局有变化,其都会走一遍,也就是说仅仅要你布局有变化,他们就会运行,所以出现了一些问题。所以在这两个方法中设置监听是不太好的。

    那怎么样技能自己主动获得孩子数。又能给相应的孩子设置上监听呢?

    于是我就去看了一下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 android.widget;
    
    import com.android.internal.R;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.util.AttributeSet;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.accessibility.AccessibilityEvent;
    import android.view.accessibility.AccessibilityNodeInfo;
    
    
    /**
     * <p>This class is used to create a multiple-exclusion scope for a set of radio
     * buttons. Checking one radio button that belongs to a radio group unchecks
     * any previously checked radio button within the same group.</p>
     *
     * <p>Intially, all of the radio buttons are unchecked. While it is not possible
     * to uncheck a particular radio button, the radio group can be cleared to
     * remove the checked state.</p>
     *
     * <p>The selection is identified by the unique id of the radio button as defined
     * in the XML layout file.</p>
     *
     * <p><strong>XML Attributes</strong></p>
     * <p>See {@link android.R.styleable#RadioGroup RadioGroup Attributes}, 
     * {@link android.R.styleable#LinearLayout LinearLayout Attributes},
     * {@link android.R.styleable#ViewGroup ViewGroup Attributes},
     * {@link android.R.styleable#View View Attributes}</p>
     * <p>Also see
     * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}
     * for layout attributes.</p>
     * 
     * @see RadioButton
     *
     */
    public class RadioGroup 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;
    
        /**
         * {@inheritDoc}
         */
        public RadioGroup(Context context) {
            super(context);
            setOrientation(VERTICAL);
            init();
        }
    
        /**
         * {@inheritDoc}
         */
        public RadioGroup(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            // retrieve selected radio button as requested by the user in the
            // XML layout file
            TypedArray attributes = context.obtainStyledAttributes(
                    attrs, com.android.internal.R.styleable.RadioGroup, com.android.internal.R.attr.radioButtonStyle, 0);
    
            int value = attributes.getResourceId(R.styleable.RadioGroup_checkedButton, View.NO_ID);
            if (value != View.NO_ID) {
                mCheckedId = value;
            }
    
            final int index = attributes.getInt(com.android.internal.R.styleable.RadioGroup_orientation, VERTICAL);
            setOrientation(index);
    
            attributes.recycle();
            init();
        }
    
        private void init() {
            mChildOnCheckedChangeListener = new CheckedStateTracker();
            mPassThroughListener = new PassThroughHierarchyChangeListener();
            super.setOnHierarchyChangeListener(mPassThroughListener);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
            // the user listener is delegated to our pass-through listener
            mPassThroughListener.mOnHierarchyChangeListener = listener;
        }
    
        /**
         * {@inheritDoc}
         */
        @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());
                }
            }
    
            super.addView(child, index, params);
        }
    
        /**
         * <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()
         *
         * @attr ref android.R.styleable#RadioGroup_checkedButton
         */
        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 RadioGroup.LayoutParams(getContext(), attrs);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
            return p instanceof RadioGroup.LayoutParams;
        }
    
        @Override
        protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        }
    
        @Override
        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
            super.onInitializeAccessibilityEvent(event);
            event.setClassName(RadioGroup.class.getName());
        }
    
        @Override
        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
            super.onInitializeAccessibilityNodeInfo(info);
            info.setClassName(RadioGroup.class.getName());
        }
    
        /**
         * <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(RadioGroup 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;
    
            /**
             * {@inheritDoc}
             */
            public void onChildViewAdded(View parent, View child) {
                if (parent == RadioGroup.this && child instanceof RadioButton) {
                    int id = child.getId();
                    // generates an id if it's missing
                    if (id == View.NO_ID) {
                        id = View.generateViewId();
                        child.setId(id);
                    }
                    ((RadioButton) child).setOnCheckedChangeWidgetListener(
                            mChildOnCheckedChangeListener);
                }
    
                if (mOnHierarchyChangeListener != null) {
                    mOnHierarchyChangeListener.onChildViewAdded(parent, child);
                }
            }
    
            /**
             * {@inheritDoc}
             */
            public void onChildViewRemoved(View parent, View child) {
                if (parent == RadioGroup.this && child instanceof RadioButton) {
                    ((RadioButton) child).setOnCheckedChangeWidgetListener(null);
                }
    
                if (mOnHierarchyChangeListener != null) {
                    mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
                }
            }
        }
    }
    


     

    这就是RadioGroup的源代码,加上凝视什么的一共400来行代码,所以不是非常多,然后就開始看源代码。当中一開始在init()中,有这么一个监听。 

    mPassThroughListener = new PassThroughHierarchyChangeListener();
            super.setOnHierarchyChangeListener(mPassThroughListener);

    然后找到其相应的监听,

     private class PassThroughHierarchyChangeListener implements
                ViewGroup.OnHierarchyChangeListener {
            private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener;
    
            /**
             * {@inheritDoc}
             */
            public void onChildViewAdded(View parent, View child) {
                if (parent == RadioGroup.this && child instanceof RadioButton) {
                    int id = child.getId();
                    // generates an id if it's missing
                    if (id == View.NO_ID) {
                        id = View.generateViewId();
                        child.setId(id);
                    }
                    ((RadioButton) child).setOnCheckedChangeWidgetListener(
                            mChildOnCheckedChangeListener);
                }
    
                if (mOnHierarchyChangeListener != null) {
                    mOnHierarchyChangeListener.onChildViewAdded(parent, child);
                }
            }
    
            /**
             * {@inheritDoc}
             */
            public void onChildViewRemoved(View parent, View child) {
                if (parent == RadioGroup.this && child instanceof RadioButton) {
                    ((RadioButton) child).setOnCheckedChangeWidgetListener(null);
                }
    
                if (mOnHierarchyChangeListener != null) {
                    mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
                }
            }
        }


    这个监听继承了ViewGroup.OnHierarchyChangeListener接口。你通过查资料你就会发现,这个监听接口是对其 层次结构的监听,不像
    onMeasure或onLayout,他仅仅有在层次结构发生变化时,也就是说在加入view和移除view的时候才会调用,所以他有两个回调方法。onChildViewAdded和onChildViewRemoved。

    在初始化的时候,子布局是一个一个的被加入到RadioGroup,所以调用onChildViewAdded,能够得带其相应的孩子。就能够对其设置监听了。

    这样设置就不会出现反复运行等问题。

    然后这一步攻克了就好说了。下一把就是我们触发监听后,要这几个控件协调控制,由于仅仅有一个是能被选中。所以在监听的时候我们应该去改变子控件的状态等。

    这一步就不细说了,应该都会顺着理清楚,就是仅仅保持一个被选中就能够。附上代码,就是依据需求略微修改了一下RadioGroup

    public class RechargeValueGroup extends LinearLayout {
    
    	private ArrayList<Integer> tabChildsId = new ArrayList<Integer>();
    
    	private OnItemTabClickListener listener;
    	private PassThroughHierarchyChangeListener mPassThroughListener;
    	private CheckedStateTracker mChildOnCheckedChangeListener;
    
    	/**
    	 * 记录当前id,默认id为-1
    	 */
    	private int mCheckedId = -1;
    
    	public RechargeValueGroup(Context context, AttributeSet attrs,
    			int defStyle) {
    		super(context, attrs, defStyle);
    	}
    
    	public RechargeValueGroup(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		init();
    	}
    
    	public RechargeValueGroup(Context context) {
    		super(context);
    	}
    
    	private void init() {
    		// radiobutton的监听
    		mChildOnCheckedChangeListener = new CheckedStateTracker();
    
    		// 层次结构的监听
    		mPassThroughListener = new PassThroughHierarchyChangeListener();
    		// 设置监听
    		super.setOnHierarchyChangeListener(mPassThroughListener);
    	}
    
    	/**
    	 * 设置tab数据
    	 * 
    	 * @param datas
    	 */
    	public void setDataOfTabs(List<RechargeNumInfo> datas) {
    		Log.i("TAG2","getChildCount()===>>>"+getChildCount());
    		Log.i("TAG2","datas()===>>>"+datas.size());
    		int count = 0;
    		if (datas.size() >= getChildCount()) {
    			count = getChildCount();
    //			return;
    		}else{
    			count = datas.size();
    		}
    		for (int i = 0; i < count; i++) {
    			Log.i("TAG2","data=1111==>>>");
    			RechargeValueLayout view = (RechargeValueLayout) findViewById(tabChildsId.get(i));
    			RechargeNumInfo data = datas.get(i);
    			Log.i("TAG2","data===>>>"+data);
    			view.setMoneyValue(data.chargeNum);
    			view.setCordsValue(data.gzyNum);
    			view.setZengsValue(data.giftNum);
    		}
    
    	}
    
    	/**
    	 * {@inheritDoc}
    	 */
    	@Override
    	protected void onFinishInflate() {
    		super.onFinishInflate();
    		Log.i("TAG2", "onFinishInflate=111==>>>>");
    		check(mCheckedId);
    	}
    
    	/**
    	 * 改变item的选择状态
    	 * 
    	 * @param viewId
    	 * @param checked
    	 *            true 为选择。false 为不选择
    	 */
    	private void setCheckedStateForView(int viewId, boolean checked) {
    		Log.i("TAG2", "setCheckedStateForView====>>>");
    		View checkedView = findViewById(viewId);
    		Log.i("TAG2", "setCheckedStateForView===checkedView=>>>" + checkedView);
    		if (checkedView != null && checkedView instanceof RechargeValueLayout) {
    			((RechargeValueLayout) checkedView).setChecked(checked);
    			Log.i("TAG2", "setCheckedStateForView===checkedView=111>>>"
    					+ checkedView);
    		}
    	}
    
    	public void check(int id) {
    		// don't even bother
    		Log.i("TAG2", "check=000======>>>" + id);
    		Log.i("TAG2", "check=000=11=====>>>" + mCheckedId);
    		if (id != -1 && (id == mCheckedId)) {
    			Log.i("TAG2", "check=111======>>>" + id);
    			return;
    		}
    
    		if (mCheckedId != -1) {
    			Log.i("TAG2", "check=222======>>>" + id);
    			setCheckedStateForView(mCheckedId, false);
    		}
    
    		if (id != -1) {
    			Log.i("TAG2", "check=333======>>>" + id);
    			setCheckedStateForView(id, true);
    		}
    
    		setCheckedId(id);
    	}
    
    	/**
    	 * 选择Item后要运行的操作
    	 * 
    	 * @param id
    	 */
    	private void setCheckedId(int id) {
    		// 更新当前选择的Item的id
    		mCheckedId = id;
    		if (listener != null) {
    			RechargeValueLayout view = (RechargeValueLayout)findViewById(id);
    			
    			listener.onItemClick(this, id,view.getMoneyValue());
    		}
    	}
    
    	/**
    	 * 设置点击item的监听
    	 * 
    	 * @param l
    	 */
    	public void setOnItemTabClickListener(OnItemTabClickListener l) {
    		this.listener = l;
    	}
    
    	/**
    	 * item选择监听 Title: RechargeValueGroup.java Description:
    	 * 
    	 * @author Liusong
    	 * @date 2015-6-26
    	 * @version V1.0
    	 */
    	public interface OnItemTabClickListener {
    		public void onItemClick(RechargeValueGroupview, int checkedId,String money);
    	}
    
    	/**
    	 * 图层变换监听 Title: RechargeValueGroup.java Description:
    	 * 
    	 * @author Liusong
    	 * @date 2015-6-26
    	 * @version V1.0
    	 */
    	private class PassThroughHierarchyChangeListener implements
    			ViewGroup.OnHierarchyChangeListener {
    		private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener;
    
    		/**
    		 * {@inheritDoc}
    		 */
    		public void onChildViewAdded(View parent, View child) {
    			if (parent == RechargeValueTabHost.this
    					&& child instanceof RechargeValueLayout) {
    				int id = child.getId();
    //				Log.i("TAG2", "onChildViewAdded=111==>>>>" + id);
    				// generates an id if it's missing
    				if (id == View.NO_ID) {
    					id = View.generateViewId();
    					child.setId(id);
    				}
    				tabChildsId.add(id);
    				((RechargeValueLayout) child)
    						.setOnClickListener(mChildOnCheckedChangeListener);
    			}
    
    			if (mOnHierarchyChangeListener != null) {
    				mOnHierarchyChangeListener.onChildViewAdded(parent, child);
    			}
    		}
    
    		/**
    		 * {@inheritDoc}
    		 */
    		public void onChildViewRemoved(View parent, View child) {
    			if (parent == RechargeValueTabHost.this
    					&& child instanceof RechargeValueLayout) {
    				((RechargeValueLayout) child).setOnClickListener(null);
    				tabChildsId.remove(child.getId());
    			}
    //			Log.i("TAG2", "onChildViewRemoved=111==>>>>" + child.getId());
    			if (mOnHierarchyChangeListener != null) {
    				mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
    			}
    		}
    	}
    
    	/**
    	 * 每一项Item的点击事件 Title: RechargeValueGroup.java Description:
    	 * 
    	 * @author Liusong
    	 * @date 2015-6-26
    	 * @version V1.0
    	 */
    	class CheckedStateTracker implements OnClickListener {
    
    		@Override
    		public void onClick(View v) {
    //			setCheckedId(v.getId());
    			check(v.getId());
    		}
    
    	};
    
    //	/**
    //	 * Item的详细数据值 Title: <strong>RechargeValueGroup</strong>.java Description:
    //	 * 
    //	 * @author Liusong
    //	 * @date 2015-6-26
    //	 * @version V1.0
    //	 */
    //	class RechargeValueBean {
    //		public int zengs;
    //		public int money;
    //		public int cords;
    //	}
    }


    就上上面的代码,因为我是使用的在Xml里面加入子控件,所以就没写addView方法,有兴趣的能够自己写一下addview方法。

    然后,我有加了一个public void setDataOfTabs(List<RechargeNumInfo> datas)这种方法,就是把得到的数据直接付给这个控件。这样,就会自己主动把全部子控件附上值。

    这样比較方便。由于数据是从server拿到的,使用Gson工具类,直接把数据封装成了集合。仅仅要把集合传入就可实现自己主动赋值。很方便,自己不须要一个一个的去赋值了。

    依据RadioCroup改动的,仅仅要依据不同的情况稍作改动就能够满足非常多情况,比如。我们常见的底部导航。tab选择等等,仅仅要我们稍把不同的item子控件改动一下,我们就会非常快的实现一个单选样式的button组。

    用法也非常easy。在代码中

    mRechargeGroup= (RechargeValueGroup) findViewById(R.id.rechargeGroup);
      mRechargeGroup.setOnItemTabClickListener(new OnItemTabClickListener() {
    
       @Override
       public void onItemClick(RechargeValueGroupview, int checkedId,String money) {
        // TODO Auto-generated method stub
        Log.i("TAG2", "checkedId===>>>"+checkedId);
        realPrice = money;
       }
       
      });
    


    找到控件设置监听,然后回掉回来各种參数,id等,我们也能够依据自己的需求去改动OnItemTabClickListener监听接口中的onItemClick传递的參数来满足自己的需求,

    然后我们在适当的地方调用setDataOfTabs(List<RechargeNumInfo> datas)这种方法来设置数据就好了。

    xml里面,就是上面的那段代码

    <com.song.components.RechargeValueGroup
                android:id="@+id/rechargeGroup"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" >
    
                <com.song.components.RechargeValueLayout
                    android:id="@+id/tab1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1" >
                </com.song.components.RechargeValueLayout>
    
                <com.song.components.RechargeValueLayout
                    android:id="@+id/tab2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1" >
                </com.song.components.RechargeValueLayout>
    
                <com.song.components.RechargeValueLayout
                    android:id="@+id/tab3"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1" >
                </com.song.components.RechargeValueLayout>
                <!--
                <com.song.components.RechargeValueLayout
                     android:id="@+id/tab4"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1" >
                </com.song.components.RechargeValueLayout>
                -->
            </com.song.components.RechargeValueGroup>


    就这么简单,大家能够在这个基础上来进行其它扩展什么的。

  • 相关阅读:
    LeetCode Convert Sorted List to Binary Search Tree
    LeetCode Convert Sorted Array to Binary Search Tree
    LeetCode Restore IP Addresses
    Linux-NoSQL之memcached
    Linux-LAMP虚拟主机配置
    Linux-Discuz安装LAMP
    python-Django与Nginx整合gunicorn模块
    python-Django与Apache整合wsgi模块
    python-Django收集主机信息json格式
    Linux-监控与安全运维之zabbix
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/5089736.html
Copyright © 2011-2022 走看看