zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

介绍几个好用的android自定义控件详解手机开发

Android手机开发 详解 介绍 自定义 控件 几个
2023-06-13 09:20:13 时间
复制代码
 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 */ 

 17 package com.newgame.sdk.view; 

 19 import java.util.ArrayList; 

 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; 

 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; 

 41 // 存放当前的radioButton 

 42 private ArrayList RadioButton radioButtons; 

 44 public FlowRadioGroup(Context context) { 

 45 super(context); 

 46 setOrientation(VERTICAL); 

 47 init(); 

 48 } 

 50 public FlowRadioGroup(Context context, AttributeSet attrs) { 

 51 super(context, attrs); 

 52 init(); 

 53 } 

 55 private void init() { 

 56 mChildOnCheckedChangeListener = new CheckedStateTracker(); 

 57 mPassThroughListener = new PassThroughHierarchyChangeListener(); 

 58 super.setOnHierarchyChangeListener(mPassThroughListener); 

 59 radioButtons = new ArrayList RadioButton (); 

 60 } 

 62 @Override 

 63 public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) { 

 64 // the user listener is delegated to our pass-through listener 

 65 mPassThroughListener.mOnHierarchyChangeListener = listener; 

 66 } 

 68 @Override 

 69 protected void onFinishInflate() { 

 70 super.onFinishInflate(); 

 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 } 

 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); 

 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 } 

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

102 } 

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 } 

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 its 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 } 

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 } 

163 /** 返回当前radiobutton控件的count */ 

164 public int getRadioButtonCount() { 

165 return radioButtons.size(); 

166 } 

168 /** 返回当前index的radio */ 

169 public RadioButton getRadioButton(int index) { 

170 return radioButtons.get(index); 

171 } 

173 /** 

174 * p 

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 * /p 

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 // dont even bother 

188 if (id != -1 (id == mCheckedId)) { 

189 return; 

190 } 

192 if (mCheckedId != -1) { 

193 setCheckedStateForView(mCheckedId, false); 

194 } 

196 if (id != -1) { 

197 setCheckedStateForView(id, true); 

198 } 

200 setCheckedId(id); 

201 } 

203 private void setCheckedId(int id) { 

204 mCheckedId = id; 

205 if (mOnCheckedChangeListener != null) { 

206 mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId); 

207 } 

208 } 

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 } 

217 /** 

218 * p 

219 * Returns the identifier of the selected radio button in this group. Upon 

220 * empty selection, the returned value is -1. 

221 * /p 

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 } 

232 /** 

233 * p 

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 * /p 

238 * 

239 * @see #check(int) 

240 * @see #getCheckedRadioButtonId() 

241 */ 

242 public void clearCheck() { 

243 check(-1); 

244 } 

246 /** 

247 * p 

248 * Register a callback to be invoked when the checked radio button changes 

249 * in this group. 

250 * /p 

251 * 

252 * @param listener 

253 * the callback to call on checked state change 

254 */ 

255 public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { 

256 mOnCheckedChangeListener = listener; 

257 } 

259 /** 

260 * {@inheritDoc} 

261 */ 

262 @Override 

263 public LayoutParams generateLayoutParams(AttributeSet attrs) { 

264 return new FlowRadioGroup.LayoutParams(getContext(), attrs); 

265 } 

267 /** 

268 * {@inheritDoc} 

269 */ 

270 @Override 

271 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 

272 return p instanceof FlowRadioGroup.LayoutParams; 

273 } 

275 @Override 

276 protected LinearLayout.LayoutParams generateDefaultLayoutParams() { 

277 return new LayoutParams(LayoutParams.WRAP_CONTENT, 

278 LayoutParams.WRAP_CONTENT); 

279 } 

281 /** 

282 * p 

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 * /p 

287 * 

288 * p 

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 * /p 

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 } 

303 /** 

304 * {@inheritDoc} 

305 */ 

306 public LayoutParams(int w, int h) { 

307 super(w, h); 

308 } 

310 /** 

311 * {@inheritDoc} 

312 */ 

313 public LayoutParams(int w, int h, float initWeight) { 

314 super(w, h, initWeight); 

315 } 

317 /** 

318 * {@inheritDoc} 

319 */ 

320 public LayoutParams(ViewGroup.LayoutParams p) { 

321 super(p); 

322 } 

324 /** 

325 * {@inheritDoc} 

326 */ 

327 public LayoutParams(MarginLayoutParams source) { 

328 super(source); 

329 } 

331 /** 

332 * p 

333 * Fixes the childs width to 

334 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and the 

335 * childs height to 

336 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} when not 

337 * specified in the XML file. 

338 * /p 

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) { 

351 if (a.hasValue(widthAttr)) { 

352 width = a.getLayoutDimension(widthAttr, "layout_width"); 

353 } else { 

354 width = WRAP_CONTENT; 

355 } 

357 if (a.hasValue(heightAttr)) { 

358 height = a.getLayoutDimension(heightAttr, "layout_height"); 

359 } else { 

360 height = WRAP_CONTENT; 

361 } 

362 } 

363 } 

365 /** 

366 * p 

367 * Interface definition for a callback to be invoked when the checked radio 

368 * button changed in this group. 

369 * /p 

370 */ 

371 public interface OnCheckedChangeListener { 

372 /** 

373 * p 

374 * Called when the checked radio button has changed. When the selection 

375 * is cleared, checkedId is -1. 

376 * /p 

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 } 

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 } 

395 mProtectFromCheckedChange = true; 

396 if (mCheckedId != -1) { 

397 setCheckedStateForView(mCheckedId, false); 

398 } 

399 mProtectFromCheckedChange = false; 

401 int id = buttonView.getId(); 

402 setCheckedId(id); 

403 } 

404 } 

406 /** 

407 * p 

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 * /p 

412 */ 

413 private class PassThroughHierarchyChangeListener implements 

414 ViewGroup.OnHierarchyChangeListener { 

415 private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener; 

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 its 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 } 

433 if (mOnHierarchyChangeListener != null) { 

434 mOnHierarchyChangeListener.onChildViewAdded(parent, child); 

435 } 

436 } 

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类型


复制代码
/** 查找复合控件并设置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 its 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); 

 }
复制代码
复制代码
 1 package com.newgame.sdk.view; 

 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; 

 10 /** 

 11 * 分割输入框 

 12 * 

 13 * @author Administrator 

 14 * 

 15 */ 

 16 public class DivisionEditText extends EditText { 

 18 /* 每组的长度 */ 

 19 private Integer eachLength = 4; 

 20 /* 分隔符 */ 

 21 private String delimiter = " "; 

 23 private String text = ""; 

 25 public DivisionEditText(Context context) { 

 26 super(context); 

 27 init(); 

 28 } 

 30 public DivisionEditText(Context context, AttributeSet attrs) { 

 31 super(context, attrs); 

 32 init(); 

 34 } 

 36 public DivisionEditText(Context context, AttributeSet attrs, int defStyle) { 

 37 super(context, attrs, defStyle); 

 38 init(); 

 39 } 

 41 /** 

 42 * 初始化 

 43 */ 

 44 public void init() { 

 46 // 内容变化监听 

 47 this.addTextChangedListener(new DivisionTextWatcher()); 

 48 // 获取焦点监听 

 49 this.setOnFocusChangeListener(new DivisionFocusChangeListener()); 

 50 } 

 52 /** 

 53 * 文本监听 

 54 * 

 55 * @author Administrator 

 56 * 

 57 */ 

 58 private class DivisionTextWatcher implements TextWatcher { 

 60 @Override 

 61 public void afterTextChanged(Editable s) { 

 62 } 

 64 @Override 

 65 public void beforeTextChanged(CharSequence s, int start, int count, 

 66 int after) { 

 67 } 

 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 } 

101 /** 

102 * 获取焦点监听 

103 * 

104 * @author Administrator 

105 * 

106 */ 

107 private class DivisionFocusChangeListener implements OnFocusChangeListener { 

109 @Override 

110 public void onFocusChange(View v, boolean hasFocus) { 

111 if (hasFocus) { 

112 // 设置焦点 

113 setSelection(getText().toString().length()); 

114 } 

115 } 

116 } 

118 /** 得到每组个数 */ 

119 public Integer getEachLength() { 

120 return eachLength; 

121 } 

123 /** 设置每组个数 */ 

124 public void setEachLength(Integer eachLength) { 

125 this.eachLength = eachLength; 

126 } 

128 /** 得到间隔符 */ 

129 public String getDelimiter() { 

130 return delimiter; 

131 } 

133 /** 设置间隔符 */ 

134 public void setDelimiter(String delimiter) { 

135 this.delimiter = delimiter; 

136 } 

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

5676.html

app程序应用开发手机开发无线开发移动端开发