android switch控件开发问题 xml布局中的 Switch 按钮

&&&&&&正文
Android开发:关于Android Settings中的八个问题
摘要:Android开发:关于Android Settings中的八个问题 - Lefter
本问将回答以下八个问题,如有错误,敬请批评指正,不胜感激!(注:本文中的Settings解析基于android4.0+)问题一、Settings的主界面是怎么实现的?问题二、为什么使用hierarchyviewer 时Settings中的很多界面显示的都是SubSettings?问题三、hierarchyviewer 中显示SubSetting时如何确定我进入的是哪个fragment?问题四、点击设置界面的某一个header时,设置界面是如何切换的?问题五、Settings.java中getMetaData与getStartingFragmentClass这两个函数是否有点矛盾?问题六、Settings的shortcut是如何创建的?从shortcut进入Settings的流程是什么?问题七、为什么我从Settings的shortcut进入时,hierarchyviewer显示的就不是SubSettings(如Data usage)?问题八、Settings.java中很多继承自它的内部类都是空实现,为什么要写这些类?-----------------------------------------------------------------------------------------------------------------------------------------由于项目需要,本人就对Android中的Settings进行了解析,希望能帮到对Settings有兴趣的同志们~-----------------------------------------------------------------------------------------------------------------------------------------问题一、Settings的主界面是怎么实现的?为了能适应平板和手机,Settings采用了PreferenceActivity和PreferenceFragment结合的实现方式。Settings.java继承自PreferenceActivity,是Settings的主界面,它通过loadHeadersFromResource函数(api level 11)加载res/xml/settings_headers.xml来构造界面。在settings_headers.xml中声明了要在Settings主界面显示的各个header(如Sound、Display等)。Settings.HeaderAdapter将其中的header分为三类。在Settings.HeaderAdapter中的getView方法中根据header的类型使用不同的布局文件。为header划分类型的函数 & & & &static int getHeaderType(Header header) { & & & & & &if (header.fragment == null && header.intent == null) { & & & & & & & &return HEADER_TYPE_CATEGORY; // 因为没有指明fragment和intent & & & & & &} else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings || header.id == R.id.mobiledata_settings) { & & & & & & & &return HEADER_TYPE_SWITCH; // 针对特定的三个header,分别为Wi-Fi、Bluetooth和Mobile data & & & & & &} else { & & & & & & & &return HEADER_TYPE_NORMAL; & & & & & &} & & & &}当我们点击主界面的header后会显示与该header相关的设置界面。大部分(如Display的详细设置界面)都是通过继承PreferenceFragment来实现的;有一部分是在settings_headers.xml中声明&intent&,当被点击时(触发PreferenceActivity的onHeaderClick())将会通过startActivity来启动在&intent&节点中声明的targetClass(如设置中的Add account)。问题二、为什么使用hierarchyviewer 时Settings中的很多界面显示的都是SubSettings?要解决这个问题我们先要清楚为什么会写一个SubSettings.java继承自Settings.java?SubSettings.java中的注释很清楚的告诉了我们原因:Stub class for showing sub- we can't use the main Settings class since for our app it is a special singleTask class。原来是因为Settings.java在声明时指定了android:launchMode=&singleTask&。我们知道,要显示Fragment的内容,我们就需要为其指定一个Activity。而Settings中的很多设置界面是由PreferenceFragment来完成的,当然也需要我们指定Activity.onBuildStartFragmentIntent函数会为我们构造一个显示Fragment的Intent对象(该函数的注释写的非常明白).Settings.java重写了这个函数(注,重写时它调用了super的该方法),在为intent对象setClass时都使用SubSettings.java.(注:在settings_headers.xml指定了intent的header是不会触发onBuildStartFragmentIntent的)结果就是,Settings中大部分fragment都是使用的SubSettings这个Activity来显示。由于hierarchyviewer只是显示当前界面使用的Activity(不能显示这个界面是由哪个Fragment构造的),所以我们使用hierarchyviewer 对Settings进行观察时很多设置界面显示的是SubSettings。问题三、hierarchyviewer 中显示SubSetting时如何确定我进入的是哪个fragment?在res/xml/settings_headers.xml中声明了各个header被点击后使用的fragment。我们可以根据这个文件确定我们进入的fragment。例如,当我们点击Display时hierarchyviewer 中显示SubSetting。我们通过查找settings_headers就可知道使用的是哪个fragment。Display这个header在settings_headers.xml中的声明: & &&!-- Display --& & &&header & & & &android:id=&@+id/display_settings& & & & &android:icon=&@drawable/ic_settings_display& & & & &android:fragment=&com.android.settings.DisplaySettings& & & & &android:title=&@string/display_settings& /& header中使用 android:fragment指明使用的fragment。由此可知,Display使用的是com.android.settings.DisplaySettings这个fragment问题四、点击设置界面的某一个header时,设置界面是如何切换的?点击设置界面的header时,会触发Settings中onHeaderClick函数,主要的处理都在其父类PreferenceActivity的onHeaderClick中实现的。如果这个header指定了fragment,在mSinglePane(标识是否为小屏幕设备。如,区分手机还是平板)为true时,会调用startWithFragment方法,在startWithFragment方法中将调用onBuildStartFragmentIntent方法来构造intent对象(重要),最后使用该intent对象启动一个activity来显示fragment。以点击Settings中的Display为例.(Bluetooth同理,只不过启动的Activity变为BluetoothSettingsActivity(继承自Settings,但是没有实现重写任何方法,所以与SubSettings是一样的处理),fragment变为 com.android.settings.bluetooth.BluetoothSettings)fragment是com.android.settings.DisplaySettings,activity是com.android.settings.SubSettings.(fragment是由onHeaderClick函数传入的,activity是由onBuildStartFragmentIntent()指定的)执行startActivity后将启动SubSettings.java。即我们将会再一次执行SubSettings和PreferenceActivity的onCreate方法(因为Settings.java的onCreate方法调用了super.onCreate()),但是这次并不会进入Settings的主界面,因为我们的使用的intent对象是有很大不同的。这一次onCreate函数(PreferenceActivity)中的initialFragment 将被初始化为com.android.settings.DisplaySettings,然后我们将进入switchToHeader(),最后switchToHeaderInner会取得FragmentTransaction对象,然后执行了transaction.replace(com.android.internal.R.id.prefs, f).就这样把我们的fragment显示出来了。在onCreate中会对其他view的visibility进行设置,以保证只显示prefs。如,将com.android.internal.R.id.headers的visibility设置为VIEW.GONE.注:PreferenceActivity的布局文件为preference_list_content.xml问题五、Settings.java中getMetaData与getStartingFragmentClass这两个函数是否有点矛盾?这两个函数可以说是相辅相成的(好官方,^_^)。getMetaData会从AndroidManifest.xml中读取Activity的&meta-data&节点的数据;getStartingFragmentClass则从启动Activity的intent中读取数据。这两个函数会对读取到的数据进行整合,getStartingFragmentClass依赖于getMetaData读取到的数据,但是它也可能对数据作出修改(为了兼容*,如对原有manage apps类进行特殊处理)。问题六、Settings的shortcut是如何创建的?从shortcut进入Settings的流程是什么?创建Settings的shortcut时Luancher将会启动CreateShortcut,创建shortcut所需的intent对象将会由CreateShortcut和其父类LuancherActivity共同构建(详见 CreateShortcut的onListItemClick),这时创建的Intent对象使用的就不是SubSettings了(LuancherActivity中intentForPosition函数执行setClassName()时使用的参数并不是SubSettings).CreateShortcut中列出了可以创建shortcut的设置项,这些设置项怎样检索出来的?原来,在创建LuancherActivity的ActivityAdapter对象时,其构造函数中执行了makeListItems函数,该函数将使用PackageManager的queryIntentActivities来根据intent对象查询符合条件的activity。使用的intent是从getTargetIntent函数返回的。不难发现,要想在CreateShortcut中显示,Activity在必须要有&category android:name=&com.android.settings.SHORTCUT& /&如果我们想将Security设置项添加到shortcut列表,我们只需要在androidmanifest.xml中声明Settings$SecuritySettingsActivity部分加上&category android:name=&com.android.settings.SHORTCUT& /&即可。回到正题,点击shortcut进入Settings时,传入的Intent对象中包含了目标fragment和目标activity以及其他信息。PreferenceActivity得到了足够多的信息,因此在onCreate中将依次执行switchToHeader()-&setSelectedHeader(null)-&switchToHeaderInner()-&transaction.replace(com.android.internal.R.id.prefs, f);这样就完成了fragment的显示(使用的activity是从intent解析出来的.在switchToHeaderInner中执行Fragment.instantiate时使用的Context是this!!)。不像执行onHeaderClick那样会执行函数onBuildStartFragmentIntent(Settings中重写了该函数)来重新指定我们使用的Activity。问题七、为什么我从Settings的shortcut进入时,hierarchyviewer显示的就不是SubSettings(如Data usage)?hierarchyviewer中显示SubSettings是因为我们在onBuildStartFragmentIntent方法中做了特殊处理(详见问题二)。从shortcut进入Settings时不显示SubSettings是因为没有走这个函数,因此就不会显示为SubSettings了(详见问题六)。问题八、Settings.java中很多继承自它的内部类都是空实现,为什么要写这些类?我们从这些内部类的名字可以看出它们的主要作用,如BluetoothSettingsActivity是针对蓝牙设置的。空实现说明他们都将使用Settings.java中的函数(注意private的属*和方法的访问权限问题)。声明这些Activity的原因我认为主要是为了提高各个设置项、整个Settings的灵活*,方便开发者进行扩展。除此之外的一些原因:可以让我们为单独的设置项添加 shortcut(如data usage),因为创建shortcut使用queryIntentActivities查询使用的允许其它程序访问单独的设置项;结构设计需要,启动Activity会读取meta-data信息;某些设置项不想使用SubSettings的属*。如,在Settings中点击Bluetooth时使用BluetoothSettingsActivity,启动Bluetooth时将使用BluetoothSettingsActivity的属*,如 android:clearTaskOnLaunch=&true&。写的不全,望补充~
全国校区查询
案例分析点击榜
案例分析最新文章
官方新版意见收集
*您的积极反馈是我们前进的动力
官方新版意见收集
提交成功,感谢您的反馈。
我们会认真阅读和考虑每个用户的反馈。实际开发中,经常会用到Dialog,比如退出时候会弹出是否退出,或者还有一些编辑框也会用Dialog实现,效果图如下:
开发中遇到的问题无非在于如果在Activity中监听这个Dialog中实现的按钮,Dialog类如下,在MyDialog这个类中实现了一个LeaveMyDialogListener接口,用来实现onclick的点击事件:
package com.Ieasy.T
import com.Ieasy.ieasyware.R;
import android.app.D
import android.content.C
import android.os.B
import android.view.V
import android.widget.B
import android.widget.TextV
public class MyDialog extends Dialog implements android.view.View.OnClickListener {
private TextV
private Button
btnok,btnedit,btncancle,
private LeaveMyDialogL
public interface LeaveMyDialogListener{
public void onClick(View view);
public MyDialog(Context context) {
super(context);
// TODO Auto-generated constructor stub
this.context =
public MyDialog(Context context,int theme,LeaveMyDialogListener listener) {
super(context,theme);
// TODO Auto-generated constructor stub
this.context =
this.listener =
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
this.setContentView(com.Ieasy.ieasyware.R.layout.mydialog);
btncancle = (Button)findViewById(R.id.mycancle);
= (Button)findViewById(R.id.myedit);
= (Button)findViewById(R.id.myok);
= (TextView)findViewById(R.id.miaosu);
= (Button)findViewById(R.id.mysave);
btncancle.setOnClickListener(this);
btnedit.setOnClickListener(this);
btnok.setOnClickListener(this);
btnsave.setOnClickListener(this);
public void onClick(View v) {
// TODO Auto-generated method stub
listener.onClick(v);
布局文件如下:
&?xml version="1.0" encoding="utf-8"?&
&LinearLayout xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" &
&LinearLayout
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:background="@drawable/night_biz_subscribe_media_recommend_item_bg"
android:orientation="vertical" &
android:id="@+id/miaosu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="20dp"
android:text="描述"
android:textColor="@color/whitesmoke"
android:textSize="20sp" /&
&LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:orientation="horizontal" &
android:id="@+id/myok"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/btnclick"
android:textColor="@color/whitesmoke"
android:text="确定" /&
android:id="@+id/myedit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/btnclick"
android:textColor="@color/whitesmoke"
android:text="编辑" /&
android:id="@+id/mysave"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/btnclick"
android:textColor="@color/whitesmoke"
android:text="保存" /&
android:id="@+id/mycancle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/btnclick"
android:textColor="@color/whitesmoke"
android:text="取消" /&
&/LinearLayout&
&/LinearLayout&
&/LinearLayout&
引用的style:
&style name="MyDialog" parent="@android:Theme.Dialog"&
&item name="android:windowFrame"&@null&/item&
&item name="android:windowNoTitle"&true&/item&
&item name="android:windowBackground"&@drawable/night_biz_subscribe_media_recommend_item_bg&/item&
&item name="android:windowIsFloating"&true&/item&
&item name="android:windowContentOverlay"&@null&/item&
最后在Activity中调用,通过LeaveMyDialogListener 接口来实现在Activity中的点击事件
MyDialog dialog = new MyDialog(context,R.style.MyDialog,
new MyDialog.LeaveMyDialogListener() {
public void onClick(View view) {
switch(view.getId()){
case R.id.myok:
case R.id.myedit:
case R.id.mycancle:
case R.id.mysave:
dialog.dismiss();
dialog.show();
如果想获得Dialog中的TextView控件可以这样获取,给TextView赋值时候一定要在Dialog show了之后在赋值,你懂得。
TextView text = (TextView) dialog.findViewById(R.id.miaosu);
阅读(...) 评论()27221人阅读
前几天在看蘑菇街上有个开关按钮:
就在想是怎样实现的,于是反编译了它的源码,但是这时得到了下面的几张图片:
图片对应的名称:
无色长条:switch_
白色圆点:switch_btn_
左白右红的长条:switch_
黑色长条:switch_mask.
那我们就用这几张图片来实现类似的效果吧。
SwitchButton类:
package com.example.
import android.content.C
import android.graphics.B
import android.graphics.BitmapF
import android.graphics.C
import android.graphics.P
import android.graphics.PorterDuff.M
import android.graphics.PorterDuffX
import android.graphics.R
import android.graphics.RectF;
import android.util.AttributeS
import android.view.MotionE
import android.view.V
public class SwitchButton extends View implements android.view.View.OnClickListener{
private Bitmap mSwitchBottom, mSwitchThumb, mSwitchFrame, mSwitchM
private float mCurrentX = 0;
private boolean mSwitchOn =//开关默认是开着的
private int mMoveL//最大移动距离
private float mLastX = 0;//第一次按下的有效区域
private Rect mDest =//绘制的目标区域大小
private Rect mSrc =//截取源图片的大小
private int mDeltX = 0;//移动的偏移量
private Paint mPaint =
private OnChangeListener mListener =
private boolean mFlag =
public SwitchButton(Context context) {
this(context, null);
// TODO Auto-generated constructor stub
public SwitchButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// TODO Auto-generated constructor stub
public SwitchButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
* 初始化相关资源
public void init() {
mSwitchBottom = BitmapFactory.decodeResource(getResources(),
R.drawable.switch_bottom);
mSwitchThumb = BitmapFactory.decodeResource(getResources(),
R.drawable.switch_btn_pressed);
mSwitchFrame = BitmapFactory.decodeResource(getResources(),
R.drawable.switch_frame);
mSwitchMask = BitmapFactory.decodeResource(getResources(),
R.drawable.switch_mask);
setOnClickListener(this);
setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
mMoveLength = mSwitchBottom.getWidth() - mSwitchFrame.getWidth();
mDest = new Rect(0, 0, mSwitchFrame.getWidth(), mSwitchFrame.getHeight());
mSrc = new Rect();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setAlpha(255);
mPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
setMeasuredDimension(mSwitchFrame.getWidth(), mSwitchFrame.getHeight());
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (mDeltX & 0 || mDeltX == 0 && mSwitchOn) {
if(mSrc != null) {
mSrc.set(mMoveLength - mDeltX, 0, mSwitchBottom.getWidth()
- mDeltX, mSwitchFrame.getHeight());
} else if(mDeltX & 0 || mDeltX == 0 && !mSwitchOn){
if(mSrc != null) {
mSrc.set(-mDeltX, 0, mSwitchFrame.getWidth() - mDeltX,
mSwitchFrame.getHeight());
} &span style=&white-space:pre&&
&/span&//这儿是离屏缓冲,自己感觉类似双缓冲机制吧
int count = canvas.saveLayer(new RectF(mDest), null, Canvas.MATRIX_SAVE_FLAG
| Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
| Canvas.FULL_COLOR_LAYER_SAVE_FLAG
| Canvas.CLIP_TO_LAYER_SAVE_FLAG);
canvas.drawBitmap(mSwitchBottom, mSrc, mDest, null);
canvas.drawBitmap(mSwitchThumb, mSrc, mDest, null);
canvas.drawBitmap(mSwitchFrame, 0, 0, null);
canvas.drawBitmap(mSwitchMask, 0, 0, mPaint);
canvas.restoreToCount(count);
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastX = event.getX();
case MotionEvent.ACTION_MOVE:
mCurrentX = event.getX();
mDeltX = (int) (mCurrentX - mLastX);
// 如果开关开着向左滑动,或者开关关着向右滑动(这时候是不需要处理的)
if ((mSwitchOn && mDeltX & 0) || (!mSwitchOn && mDeltX & 0)) {
mDeltX = 0;
if (Math.abs(mDeltX) & mMoveLength) {
mDeltX = mDeltX & 0 ? mMoveLength : - mMoveL
invalidate();
case MotionEvent.ACTION_UP:
if (Math.abs(mDeltX) & 0 && Math.abs(mDeltX) & mMoveLength / 2) {
mDeltX = 0;
invalidate();
} else if (Math.abs(mDeltX) & mMoveLength / 2 && Math.abs(mDeltX) &= mMoveLength) {
mDeltX = mDeltX & 0 ? mMoveLength : -mMoveL
mSwitchOn = !mSwitchOn;
if(mListener != null) {
mListener.onChange(this, mSwitchOn);
invalidate();
mDeltX = 0;
} else if(mDeltX == 0 && mFlag) {
//这时候得到的是不需要进行处理的,因为已经move过了
mDeltX = 0;
return super.onTouchEvent(event);
invalidate();
return super.onTouchEvent(event);
public void setOnChangeListener(OnChangeListener listener) {
mListener =
public interface OnChangeListener {
public void onChange(SwitchButton sb, boolean state);
public void onClick(View v) {
// TODO Auto-generated method stub
mDeltX = mSwitchOn ? mMoveLength : -mMoveL
mSwitchOn = !mSwitchOn;
if(mListener != null) {
mListener.onChange(this, mSwitchOn);
invalidate();
mDeltX = 0;
MainActivity:
package com.example.
import com.example.switchbutton.SwitchButton.OnChangeL
import android.app.A
import android.os.B
import android.util.L
import android.view.M
import android.widget.T
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SwitchButton sb = (SwitchButton) findViewById(R.id.wiperSwitch1);
sb.setOnChangeListener(new OnChangeListener() {
public void onChange(SwitchButton sb, boolean state) {
// TODO Auto-generated method stub
Log.d(&switchButton&, state ? &开&:&关&);
Toast.makeText(MainActivity.this, state ? &开&:&关&, Toast.LENGTH_SHORT).show();
public boolean onCreateOptionsMenu(Menu menu) {
// I this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
activity_main.xml:
&LinearLayout xmlns:android=&/apk/res/android&
xmlns:tools=&/tools&
android:layout_width=&match_parent&
android:layout_height=&match_parent&
android:orientation=&vertical&
android:paddingBottom=&@dimen/activity_vertical_margin&
android:paddingLeft=&@dimen/activity_horizontal_margin&
android:paddingRight=&@dimen/activity_horizontal_margin&
android:paddingTop=&@dimen/activity_vertical_margin& &
&ImageView
android:id=&@+id/imageView&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content& /&
&com.example.switchbutton.SwitchButton
android:id=&@+id/wiperSwitch1&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_marginLeft=&100dip& /&
&/LinearLayout&主要的代码在switchbutton类中,代码有什么不足还希望大家多提提意见,自己很少写。
& & & & & &&
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:159350次
积分:2339
积分:2339
排名:第7821名
原创:69篇
转载:58篇
评论:84条
(3)(1)(3)(1)(2)(2)(2)(2)(8)(6)(4)(2)(6)(6)(2)(4)(2)(3)(12)(2)(1)(1)(2)(8)(3)(4)(1)(8)(2)(4)(3)(7)(5)(4)(1)}

我要回帖

更多关于 android xml布局属性 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信