介绍几个工作开发中封装的好用的android自定义控件

介绍几个工作开发中封装的好用的android自定义控件

首先看效果图,

介绍几个工作开发中封装的好用的android自定义控件_第1张图片介绍几个工作开发中封装的好用的android自定义控件_第2张图片

看下这两个界面,第一个中用到了一个自定义的FlowRadioGroup,支持复合子控件,自定义布局;

第二个界面中看到了输入的数字 自动4位分割了吧;也用到了自定义的DivisionEditText控件。

下面直接看源码FlowRadioGroup了;

复制代码
  1 /*
  2  * Copyright (C) 2006 The Android Open Source Project
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package com.newgame.sdk.view;
 18 
 19 import java.util.ArrayList;
 20 
 21 import android.content.Context;
 22 import android.content.res.TypedArray;
 23 import android.util.AttributeSet;
 24 import android.view.View;
 25 import android.view.ViewGroup;
 26 import android.widget.CompoundButton;
 27 import android.widget.LinearLayout;
 28 import android.widget.RadioButton;
 29 
 30 /** 可以放多种布局控件,能找到radiobutton */
 31 public class FlowRadioGroup extends LinearLayout {
 32     // holds the checked id; the selection is empty by default
 33     private int mCheckedId = -1;
 34     // tracks children radio buttons checked state
 35     private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
 36     // when true, mOnCheckedChangeListener discards events
 37     private boolean mProtectFromCheckedChange = false;
 38     private OnCheckedChangeListener mOnCheckedChangeListener;
 39     private PassThroughHierarchyChangeListener mPassThroughListener;
 40 
 41     // 存放当前的radioButton
 42     private ArrayList radioButtons;
 43 
 44     public FlowRadioGroup(Context context) {
 45         super(context);
 46         setOrientation(VERTICAL);
 47         init();
 48     }
 49 
 50     public FlowRadioGroup(Context context, AttributeSet attrs) {
 51         super(context, attrs);
 52         init();
 53     }
 54 
 55     private void init() {
 56         mChildOnCheckedChangeListener = new CheckedStateTracker();
 57         mPassThroughListener = new PassThroughHierarchyChangeListener();
 58         super.setOnHierarchyChangeListener(mPassThroughListener);
 59         radioButtons = new ArrayList();
 60     }
 61 
 62     @Override
 63     public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
 64         // the user listener is delegated to our pass-through listener
 65         mPassThroughListener.mOnHierarchyChangeListener = listener;
 66     }
 67 
 68     @Override
 69     protected void onFinishInflate() {
 70         super.onFinishInflate();
 71 
 72         // checks the appropriate radio button as requested in the XML file
 73         if (mCheckedId != -1) {
 74             mProtectFromCheckedChange = true;
 75             setCheckedStateForView(mCheckedId, true);
 76             mProtectFromCheckedChange = false;
 77             setCheckedId(mCheckedId);
 78         }
 79     }
 80 
 81     @Override
 82     public void addView(View child, int index, ViewGroup.LayoutParams params) {
 83         if (child instanceof RadioButton) {
 84             final RadioButton button = (RadioButton) child;
 85             radioButtons.add(button);
 86 
 87             if (button.isChecked()) {
 88                 mProtectFromCheckedChange = true;
 89                 if (mCheckedId != -1) {
 90                     setCheckedStateForView(mCheckedId, false);
 91                 }
 92                 mProtectFromCheckedChange = false;
 93                 setCheckedId(button.getId());
 94             }
 95         } else if (child instanceof ViewGroup) {// 如果是复合控件
 96             // 遍历复合控件
 97             ViewGroup vg = ((ViewGroup) child);
 98             setCheckedView(vg);
 99         }
100 
101         super.addView(child, index, params);
102     }
103 
104     /** 查找复合控件并设置radiobutton */
105     private void setCheckedView(ViewGroup vg) {
106         int len = vg.getChildCount();
107         for (int i = 0; i < len; i++) {
108             if (vg.getChildAt(i) instanceof RadioButton) {// 如果找到了,就设置check状态
109                 final RadioButton button = (RadioButton) vg.getChildAt(i);
110                 // 添加到容器
111                 radioButtons.add(button);
112                 if (button.isChecked()) {
113                     mProtectFromCheckedChange = true;
114                     if (mCheckedId != -1) {
115                         setCheckedStateForView(mCheckedId, false);
116                     }
117                     mProtectFromCheckedChange = false;
118                     setCheckedId(button.getId());
119                 }
120             } else if (vg.getChildAt(i) instanceof ViewGroup) {// 迭代查找并设置
121                 ViewGroup childVg = (ViewGroup) vg.getChildAt(i);
122                 setCheckedView(childVg);
123             }
124         }
125     }
126 
127     /** 查找复合控件并设置id */
128     private void setCheckedId(ViewGroup vg) {
129         int len = vg.getChildCount();
130         for (int i = 0; i < len; i++) {
131             if (vg.getChildAt(i) instanceof RadioButton) {// 如果找到了,就设置check状态
132                 final RadioButton button = (RadioButton) vg.getChildAt(i);
133                 int id = button.getId();
134                 // generates an id if it's missing
135                 if (id == View.NO_ID) {
136                     id = button.hashCode();
137                     button.setId(id);
138                 }
139                 button.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
140             } else if (vg.getChildAt(i) instanceof ViewGroup) {// 迭代查找并设置
141                 ViewGroup childVg = (ViewGroup) vg.getChildAt(i);
142                 setCheckedId(childVg);
143             }
144         }
145     }
146 
147     /** 查找radioButton控件 */
148     public RadioButton findRadioButton(ViewGroup group) {
149         RadioButton resBtn = null;
150         int len = group.getChildCount();
151         for (int i = 0; i < len; i++) {
152             if (group.getChildAt(i) instanceof RadioButton) {
153                 resBtn = (RadioButton) group.getChildAt(i);
154             } else if (group.getChildAt(i) instanceof ViewGroup) {
155                 resBtn = findRadioButton((ViewGroup) group.getChildAt(i));
156                 findRadioButton((ViewGroup) group.getChildAt(i));
157                 break;
158             }
159         }
160         return resBtn;
161     }
162 
163     /** 返回当前radiobutton控件的count */
164     public int getRadioButtonCount() {
165         return radioButtons.size();
166     }
167 
168     /** 返回当前index的radio */
169     public RadioButton getRadioButton(int index) {
170         return radioButtons.get(index);
171     }    
172 
173     /**
174      * 

175 * Sets the selection to the radio button whose identifier is passed in 176 * parameter. Using -1 as the selection identifier clears the selection; 177 * such an operation is equivalent to invoking {@link #clearCheck()}. 178 *

179 * 180 * @param id 181 * the unique id of the radio button to select in this group 182 * 183 * @see #getCheckedRadioButtonId() 184 * @see #clearCheck() 185 */ 186 public void check(int id) { 187 // don't even bother 188 if (id != -1 && (id == mCheckedId)) { 189 return; 190 } 191 192 if (mCheckedId != -1) { 193 setCheckedStateForView(mCheckedId, false); 194 } 195 196 if (id != -1) { 197 setCheckedStateForView(id, true); 198 } 199 200 setCheckedId(id); 201 } 202 203 private void setCheckedId(int id) { 204 mCheckedId = id; 205 if (mOnCheckedChangeListener != null) { 206 mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId); 207 } 208 } 209 210 private void setCheckedStateForView(int viewId, boolean checked) { 211 View checkedView = findViewById(viewId); 212 if (checkedView != null && checkedView instanceof RadioButton) { 213 ((RadioButton) checkedView).setChecked(checked); 214 } 215 } 216 217 /** 218 *

219 * Returns the identifier of the selected radio button in this group. Upon 220 * empty selection, the returned value is -1. 221 *

222 * 223 * @return the unique id of the selected radio button in this group 224 * 225 * @see #check(int) 226 * @see #clearCheck() 227 */ 228 public int getCheckedRadioButtonId() { 229 return mCheckedId; 230 } 231 232 /** 233 *

234 * Clears the selection. When the selection is cleared, no radio button in 235 * this group is selected and {@link #getCheckedRadioButtonId()} returns 236 * null. 237 *

238 * 239 * @see #check(int) 240 * @see #getCheckedRadioButtonId() 241 */ 242 public void clearCheck() { 243 check(-1); 244 } 245 246 /** 247 *

248 * Register a callback to be invoked when the checked radio button changes 249 * in this group. 250 *

251 * 252 * @param listener 253 * the callback to call on checked state change 254 */ 255 public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { 256 mOnCheckedChangeListener = listener; 257 } 258 259 /** 260 * {@inheritDoc} 261 */ 262 @Override 263 public LayoutParams generateLayoutParams(AttributeSet attrs) { 264 return new FlowRadioGroup.LayoutParams(getContext(), attrs); 265 } 266 267 /** 268 * {@inheritDoc} 269 */ 270 @Override 271 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 272 return p instanceof FlowRadioGroup.LayoutParams; 273 } 274 275 @Override 276 protected LinearLayout.LayoutParams generateDefaultLayoutParams() { 277 return new LayoutParams(LayoutParams.WRAP_CONTENT, 278 LayoutParams.WRAP_CONTENT); 279 } 280 281 /** 282 *

283 * This set of layout parameters defaults the width and the height of the 284 * children to {@link #WRAP_CONTENT} when they are not specified in the XML 285 * file. Otherwise, this class ussed the value read from the XML file. 286 *

287 * 288 *

289 * See {@link android.R.styleable#LinearLayout_Layout LinearLayout 290 * Attributes} for a list of all child view attributes that this class 291 * supports. 292 *

293 * 294 */ 295 public static class LayoutParams extends LinearLayout.LayoutParams { 296 /** 297 * {@inheritDoc} 298 */ 299 public LayoutParams(Context c, AttributeSet attrs) { 300 super(c, attrs); 301 } 302 303 /** 304 * {@inheritDoc} 305 */ 306 public LayoutParams(int w, int h) { 307 super(w, h); 308 } 309 310 /** 311 * {@inheritDoc} 312 */ 313 public LayoutParams(int w, int h, float initWeight) { 314 super(w, h, initWeight); 315 } 316 317 /** 318 * {@inheritDoc} 319 */ 320 public LayoutParams(ViewGroup.LayoutParams p) { 321 super(p); 322 } 323 324 /** 325 * {@inheritDoc} 326 */ 327 public LayoutParams(MarginLayoutParams source) { 328 super(source); 329 } 330 331 /** 332 *

333 * Fixes the child's width to 334 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and the 335 * child's height to 336 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} when not 337 * specified in the XML file. 338 *

339 * 340 * @param a 341 * the styled attributes set 342 * @param widthAttr 343 * the width attribute to fetch 344 * @param heightAttr 345 * the height attribute to fetch 346 */ 347 @Override 348 protected void setBaseAttributes(TypedArray a, int widthAttr, 349 int heightAttr) { 350 351 if (a.hasValue(widthAttr)) { 352 width = a.getLayoutDimension(widthAttr, "layout_width"); 353 } else { 354 width = WRAP_CONTENT; 355 } 356 357 if (a.hasValue(heightAttr)) { 358 height = a.getLayoutDimension(heightAttr, "layout_height"); 359 } else { 360 height = WRAP_CONTENT; 361 } 362 } 363 } 364 365 /** 366 *

367 * Interface definition for a callback to be invoked when the checked radio 368 * button changed in this group. 369 *

370 */ 371 public interface OnCheckedChangeListener { 372 /** 373 *

374 * Called when the checked radio button has changed. When the selection 375 * is cleared, checkedId is -1. 376 *

377 * 378 * @param group 379 * the group in which the checked radio button has changed 380 * @param checkedId 381 * the unique identifier of the newly checked radio button 382 */ 383 public void onCheckedChanged(FlowRadioGroup group, int checkedId); 384 } 385 386 private class CheckedStateTracker implements 387 CompoundButton.OnCheckedChangeListener { 388 public void onCheckedChanged(CompoundButton buttonView, 389 boolean isChecked) { 390 // prevents from infinite recursion 391 if (mProtectFromCheckedChange) { 392 return; 393 } 394 395 mProtectFromCheckedChange = true; 396 if (mCheckedId != -1) { 397 setCheckedStateForView(mCheckedId, false); 398 } 399 mProtectFromCheckedChange = false; 400 401 int id = buttonView.getId(); 402 setCheckedId(id); 403 } 404 } 405 406 /** 407 *

408 * A pass-through listener acts upon the events and dispatches them to 409 * another listener. This allows the table layout to set its own internal 410 * hierarchy change listener without preventing the user to setup his. 411 *

412 */ 413 private class PassThroughHierarchyChangeListener implements 414 ViewGroup.OnHierarchyChangeListener { 415 private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener; 416 417 public void onChildViewAdded(View parent, View child) { 418 if (parent == FlowRadioGroup.this && child instanceof RadioButton) { 419 int id = child.getId(); 420 // generates an id if it's missing 421 if (id == View.NO_ID) { 422 id = child.hashCode(); 423 child.setId(id); 424 } 425 ((RadioButton) child) 426 .setOnCheckedChangeListener(mChildOnCheckedChangeListener); 427 } else if (parent == FlowRadioGroup.this 428 && child instanceof ViewGroup) {// 如果是复合控件 429 // 查找并设置id 430 setCheckedId((ViewGroup) child); 431 } 432 433 if (mOnHierarchyChangeListener != null) { 434 mOnHierarchyChangeListener.onChildViewAdded(parent, child); 435 } 436 } 437 438 public void onChildViewRemoved(View parent, View child) { 439 if (parent == FlowRadioGroup.this && child instanceof RadioButton) { 440 ((RadioButton) child).setOnCheckedChangeListener(null); 441 } else if (parent == FlowRadioGroup.this 442 && child instanceof ViewGroup) { 443 findRadioButton((ViewGroup) child).setOnCheckedChangeListener( 444 null); 445 } 446 if (mOnHierarchyChangeListener != null) { 447 mOnHierarchyChangeListener.onChildViewRemoved(parent, child); 448 } 449 } 450 } 451 }
复制代码

简单讲解下我的实现:

1)在addview方法中,加上判断,当前子控件是否为viewgroup类型

复制代码
@Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (child instanceof RadioButton) {
            final RadioButton button = (RadioButton) child;
            radioButtons.add(button);//将找到的控件添加到集合中

            if (button.isChecked()) {
                mProtectFromCheckedChange = true;
                if (mCheckedId != -1) {
                    setCheckedStateForView(mCheckedId, false);
                }
                mProtectFromCheckedChange = false;
                setCheckedId(button.getId());
            }
        } else if (child instanceof ViewGroup) {// 如果是复合控件
            // 遍历复合控件
            ViewGroup vg = ((ViewGroup) child);
            setCheckedView(vg);
        }

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

    /** 查找复合控件并设置radiobutton */
    private void setCheckedView(ViewGroup vg) {
        int len = vg.getChildCount();
        for (int i = 0; i < len; i++) {
            if (vg.getChildAt(i) instanceof RadioButton) {// 如果找到了,就设置check状态
                final RadioButton button = (RadioButton) vg.getChildAt(i);
                // 添加到容器
                radioButtons.add(button);
                if (button.isChecked()) {
                    mProtectFromCheckedChange = true;
                    if (mCheckedId != -1) {
                        setCheckedStateForView(mCheckedId, false);
                    }
                    mProtectFromCheckedChange = false;
                    setCheckedId(button.getId());
                }
            } else if (vg.getChildAt(i) instanceof ViewGroup) {// 迭代查找并设置
                ViewGroup childVg = (ViewGroup) vg.getChildAt(i);
                setCheckedView(childVg);
            }
        }
    }
复制代码

 

2)定义一个数组存放当前所有查到到的radiobutton;

3)在onChildViewAdded方法中,判断新添加的子控件是否为viewgroup类型

1
2
3
4
5
else if (parent == FlowRadioGroup. this
                     && child instanceof ViewGroup) { // 如果是复合控件
                 // 查找并设置id
                 setCheckedId((ViewGroup) child);
             }

  

复制代码
/** 查找复合控件并设置id */
    private void setCheckedId(ViewGroup vg) {
        int len = vg.getChildCount();
        for (int i = 0; i < len; i++) {
            if (vg.getChildAt(i) instanceof RadioButton) {// 如果找到了,就设置check状态
                final RadioButton button = (RadioButton) vg.getChildAt(i);
                int id = button.getId();
                // generates an id if it's missing
                if (id == View.NO_ID) {
                    id = button.hashCode();
                    button.setId(id);
                }
                button.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
            } else if (vg.getChildAt(i) instanceof ViewGroup) {// 迭代查找并设置
                ViewGroup childVg = (ViewGroup) vg.getChildAt(i);
                setCheckedId(childVg);
            }
        }
    }
复制代码

 

下面是DivisionEditText的源码;

复制代码
  1 package com.newgame.sdk.view;
  2 
  3 import android.content.Context;
  4 import android.text.Editable;
  5 import android.text.TextWatcher;
  6 import android.util.AttributeSet;
  7 import android.view.View;
  8 import android.widget.EditText;
  9 
 10 /**
 11  * 分割输入框
 12  * 
 13  * @author Administrator
 14  * 
 15  */
 16 public class DivisionEditText extends EditText {
 17 
 18     /* 每组的长度 */
 19     private Integer eachLength = 4;
 20     /* 分隔符 */
 21     private String delimiter = " ";
 22 
 23     private String text = "";
 24 
 25     public DivisionEditText(Context context) {
 26         super(context);
 27         init();
 28     }
 29 
 30     public DivisionEditText(Context context, AttributeSet attrs) {
 31         super(context, attrs);
 32         init();
 33 
 34     }
 35 
 36     public DivisionEditText(Context context, AttributeSet attrs, int defStyle) {
 37         super(context, attrs, defStyle);
 38         init();
 39     }
 40 
 41     /**
 42      * 初始化
 43      */
 44     public void init() {
 45 
 46         // 内容变化监听
 47         this.addTextChangedListener(new DivisionTextWatcher());
 48         // 获取焦点监听
 49         this.setOnFocusChangeListener(new DivisionFocusChangeListener());
 50     }
 51 
 52     /**
 53      * 文本监听
 54      * 
 55      * @author Administrator
 56      * 
 57      */
 58     private class DivisionTextWatcher implements TextWatcher {
 59 
 60         @Override
 61         public void afterTextChanged(Editable s) {
 62         }
 63 
 64         @Override
 65         public void beforeTextChanged(CharSequence s, int start, int count,
 66                 int after) {
 67         }
 68 
 69         @Override
 70         public void onTextChanged(CharSequence s, int start, int before,
 71                 int count) {
 72             // 统计个数
 73             int len = s.length();
 74             if (len < eachLength)// 长度小于要求的数
 75                 return;
 76             if (count > 1) {
 77                 return;
 78             }
 79             // 如果包含空格,就清除
 80             char[] chars = s.toString().replace(" ", "").toCharArray();
 81             len = chars.length;
 82             // 每4个分组,加上空格组合成新的字符串
 83             StringBuffer sb = new StringBuffer();
 84             for (int i = 0; i < len; i++) {
 85                 if (i % eachLength == 0 && i != 0)// 每次遍历到4的倍数,就添加一个空格
 86                 {
 87                     sb.append(" ");
 88                     sb.append(chars[i]);// 添加字符
 89                 } else {
 90                     sb.append(chars[i]);// 添加字符
 91                 }
 92             }
 93             // 设置新的字符到文本
 94             // System.out.println("*************" + sb.toString());
 95             text = sb.toString();
 96             setText(text);
 97             setSelection(text.length());
 98         }
 99     }
100 
101     /**
102      * 获取焦点监听
103      * 
104      * @author Administrator
105      * 
106      */
107     private class DivisionFocusChangeListener implements OnFocusChangeListener {
108 
109         @Override
110         public void onFocusChange(View v, boolean hasFocus) {
111             if (hasFocus) {
112                 // 设置焦点
113                 setSelection(getText().toString().length());
114             }
115         }
116     }
117 
118     /** 得到每组个数 */
119     public Integer getEachLength() {
120         return eachLength;
121     }
122 
123     /** 设置每组个数 */
124     public void setEachLength(Integer eachLength) {
125         this.eachLength = eachLength;
126     }
127 
128     /** 得到间隔符 */
129     public String getDelimiter() {
130         return delimiter;
131     }
132 
133     /** 设置间隔符 */
134     public void setDelimiter(String delimiter) {
135         this.delimiter = delimiter;
136     }
137 
138 }
复制代码

上面代码实现逻辑:在TextWatcher的onTextChanged方法中判断当前输入的字符,然后没4位添加一个空格,组成新的字符

复制代码
@Override
        public void onTextChanged(CharSequence s, int start, int before,
                int count) {
            // 统计个数
            int len = s.length();
            if (len < eachLength)// 长度小于要求的数
                return;
            if (count > 1) {// 设置新字符串的时候,直接返回
                return;
            }
            // 如果包含空格,就清除
            char[] chars = s.toString().replace(" ", "").toCharArray();
            len = chars.length;
            // 每4个分组,加上空格组合成新的字符串
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < len; i++) {
                if (i % eachLength == 0 && i != 0)// 每次遍历到4的倍数,就添加一个空格
                {
                    sb.append(" ");
                    sb.append(chars[i]);// 添加字符
                } else {
                    sb.append(chars[i]);// 添加字符
                }
            }
            // 设置新的字符到文本
            // System.out.println("*************" + sb.toString());
            text = sb.toString();
            setText(text);
            setSelection(text.length());
        }
复制代码

 

还有其他两个自定义控件也在项目中,这里界面没体现出来,我已经放在项目中了;

欢迎大家找出代码中的存在bug!!!!

最 后附上代码下载地址:http://www.eoeandroid.com/forum.php?mod=attachment& aid=MTIwMDM1fDM5NTYzZjQ3fDEzOTY0Mjc4NDF8NzU4MzI1fDMyODQyNw%3D%3D

你可能感兴趣的:(移动开发,php,java)