版本:1.0 日期:2014.5.17  2014.6.1 版权:© 2014 kince 转载注明出处
   在介绍SwitchButton之前,先来看一下系统Button是如何实现的。源码如下:
@RemoteViewpublic class Button extends TextView {    public Button(Context context) {        this(context, null);    }    public Button(Context context, AttributeSet attrs) {        this(context, attrs, com.android.internal.R.attr.buttonStyle);    }    public Button(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    @Override    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {        super.onInitializeAccessibilityEvent(event);        event.setClassName(Button. class.getName());    }    @Override    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {        super.onInitializeAccessibilityNodeInfo(info);        info.setClassName(Button. class.getName());    }}
  是直接继承于TextView,所不同的是在构造方法中添加了Button的样式,并且在初始化可见性方面交由Button类自己来处理。虽然Button的实现比较简单,但是它的子类并不是这样。看一下:
  直接子类只有有一个,CompoundButton。它是一个抽象类,而实现这个类的控件正是 CheckBox RadioButton Switch ToggleButton 这四个,所以先重点说一下它。源码如下:   
/** * 

* A button with two states, checked and unchecked. When the button is pressed * or clicked, the state changes automatically. *

* *

XML attributes

*

* See {@link android.R.styleable#CompoundButton * CompoundButton Attributes}, {@link android.R.styleable#Button Button * Attributes}, {@link android.R.styleable#TextView TextView Attributes}, {@link * android.R.styleable #View View Attributes} *

*/public abstract class CompoundButton extends Button implements Checkable { private boolean mChecked ; private int mButtonResource ; private boolean mBroadcasting ; private Drawable mButtonDrawable; private OnCheckedChangeListener mOnCheckedChangeListener; private OnCheckedChangeListener mOnCheckedChangeWidgetListener ; private static final int[] CHECKED_STATE_SET = { R.attr.state_checked }; public CompoundButton(Context context) { this(context, null); } public CompoundButton(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CompoundButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.CompoundButton, defStyle, 0); Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button); if (d != null ) { setButtonDrawable(d); } boolean checked = a .getBoolean(com.android.internal.R.styleable.CompoundButton_checked, false); setChecked(checked); a.recycle(); } public void toggle() { setChecked(! mChecked); } @Override public boolean performClick() { /* * XXX: These are tiny, need some surrounding 'expanded touch area', * which will need to be implemented in Button if we only override * performClick() */ /* When clicked, toggle the state */ toggle(); return super .performClick(); } @ViewDebug.ExportedProperty public boolean isChecked() { return mChecked ; } /** *

Changes the checked state of this button.

* * @param checked true to check the button, false to uncheck it */ public void setChecked(boolean checked) { if (mChecked != checked) { mChecked = checked; refreshDrawableState(); notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED ); // Avoid infinite recursions if setChecked() is called from a listener if (mBroadcasting ) { return; } mBroadcasting = true ; if (mOnCheckedChangeListener != null) { mOnCheckedChangeListener.onCheckedChanged(this, mChecked); } if (mOnCheckedChangeWidgetListener != null) { mOnCheckedChangeWidgetListener .onCheckedChanged(this, mChecked); } mBroadcasting = false ; } } /** * Register a callback to be invoked when the checked state of this button * changes. * * @param listener the callback to call on checked state change */ public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { mOnCheckedChangeListener = listener; } /** * Register a callback to be invoked when the checked state of this button * changes. This callback is used for internal purpose only. * * @param listener the callback to call on checked state change * @hide */ void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) { mOnCheckedChangeWidgetListener = listener; } /** * Interface definition for a callback to be invoked when the checked state * of a compound button changed. */ public static interface OnCheckedChangeListener { /** * Called when the checked state of a compound button has changed. * * @param buttonView The compound button view whose state has changed. * @param isChecked The new checked state of buttonView. */ void onCheckedChanged(CompoundButton buttonView, boolean isChecked); } /** * Set the background to a given Drawable, identified by its resource id. * * @param resid the resource id of the drawable to use as the background */ public void setButtonDrawable(int resid) { if (resid != 0 && resid == mButtonResource ) { return; } mButtonResource = resid; Drawable d = null; if (mButtonResource != 0) { d = getResources().getDrawable(mButtonResource ); } setButtonDrawable(d); } /** * Set the background to a given Drawable * * @param d The Drawable to use as the background */ public void setButtonDrawable(Drawable d) { if (d != null ) { if (mButtonDrawable != null) { mButtonDrawable.setCallback(null); unscheduleDrawable( mButtonDrawable); } d.setCallback( this); d.setVisible(getVisibility() == VISIBLE, false); mButtonDrawable = d; setMinHeight(mButtonDrawable .getIntrinsicHeight()); } refreshDrawableState(); } @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); event.setClassName(CompoundButton.class .getName()); event.setChecked( mChecked); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(CompoundButton.class .getName()); info.setCheckable( true); info.setChecked( mChecked); } @Override public int getCompoundPaddingLeft() { int padding = super.getCompoundPaddingLeft(); if (!isLayoutRtl()) { final Drawable buttonDrawable = mButtonDrawable; if (buttonDrawable != null) { padding += buttonDrawable.getIntrinsicWidth(); } } return padding; } @Override public int getCompoundPaddingRight() { int padding = super.getCompoundPaddingRight(); if (isLayoutRtl()) { final Drawable buttonDrawable = mButtonDrawable; if (buttonDrawable != null) { padding += buttonDrawable.getIntrinsicWidth(); } } return padding; } /** * @hide */ @Override public int getHorizontalOffsetForDrawables() { final Drawable buttonDrawable = mButtonDrawable ; return (buttonDrawable != null) ? buttonDrawable.getIntrinsicWidth() : 0; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); final Drawable buttonDrawable = mButtonDrawable ; if (buttonDrawable != null) { final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK ; final int drawableHeight = buttonDrawable.getIntrinsicHeight(); final int drawableWidth = buttonDrawable.getIntrinsicWidth(); int top = 0; switch (verticalGravity) { case Gravity.BOTTOM : top = getHeight() - drawableHeight; break; case Gravity.CENTER_VERTICAL : top = (getHeight() - drawableHeight) / 2; break; } int bottom = top + drawableHeight; int left = isLayoutRtl() ? getWidth() - drawableWidth : 0; int right = isLayoutRtl() ? getWidth() : drawableWidth; buttonDrawable.setBounds(left, top, right, bottom); buttonDrawable.draw(canvas); } } @Override protected int[] onCreateDrawableState(int extraSpace) { final int [] drawableState = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) { mergeDrawableStates(drawableState, CHECKED_STATE_SET); } return drawableState; } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mButtonDrawable != null) { int[] myDrawableState = getDrawableState(); // Set the state of the Drawable mButtonDrawable.setState(myDrawableState); invalidate(); } } @Override protected boolean verifyDrawable(Drawable who) { return super .verifyDrawable(who) || who == mButtonDrawable; } @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); if (mButtonDrawable != null) mButtonDrawable.jumpToCurrentState(); } static class SavedState extends BaseSavedState { boolean checked ; /** * Constructor called from {@link CompoundButton#onSaveInstanceState()} */ SavedState(Parcelable superState) { super(superState); } /** * Constructor called from {@link #CREATOR} */ private SavedState(Parcel in) { super(in); checked = (Boolean)in.readValue( null); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeValue( checked); } @Override public String toString() { return "CompoundButton.SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " checked=" + checked + "}" ; } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } @Override public Parcelable onSaveInstanceState() { // Force our ancestor class to save its state setFreezesText( true); Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss. checked = isChecked(); return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); setChecked(ss. checked); requestLayout(); }}
   先从构造方法开始,在构造方法中,
 public CompoundButton(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        TypedArray a =                context.obtainStyledAttributes(                        attrs, com.android.internal.R.styleable.CompoundButton, defStyle, 0);        Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button);        if (d != null ) {            setButtonDrawable(d);        }        boolean checked = a                .getBoolean(com.android.internal.R.styleable.CompoundButton_checked, false);        setChecked(checked);        a.recycle();    }  
   先是从attrs中读取定义的属性,一个是Drawable用于设置背景;一个是布尔类型变量用于判断是否check过。设置背景使用的是setButtonDrawable()方法,代码如下:
/**     * Set the background to a given Drawable     *     * @param d The Drawable to use as the background     */    public void setButtonDrawable(Drawable d) {        if (d != null ) {            if (mButtonDrawable != null) {                mButtonDrawable.setCallback(null);                unscheduleDrawable( mButtonDrawable);            }            d.setCallback( this);            d.setVisible(getVisibility() == VISIBLE, false);            mButtonDrawable = d;            setMinHeight(mButtonDrawable .getIntrinsicHeight());        }        refreshDrawableState();    }
  这个方法写的就比较完善,可以作为一个学习的典范。首先判断传递过来的Drawable是否为空,如果不为空并且默认的Drawable也不为空,那么取消默认Drawable的callback,然后调用 unscheduleDrawable 方法。这个方法代码如下:
 /**     * Unschedule any events associated with the given Drawable.  This can be     * used when selecting a new Drawable into a view, so that the previous     * one is completely unscheduled.     *     * @param who The Drawable to unschedule.     *     * @see #drawableStateChanged     */    public void unscheduleDrawable(Drawable who) {        if (mAttachInfo != null && who != null) {            mAttachInfo.mViewRootImpl .mChoreographer.removeCallbacks(                    Choreographer.CALLBACK_ANIMATION, null, who);        }    }
   从方法注释中可以看出它的用途,正是更换Drawable时候使用的。接下来开始重新设置Drawable,包括回调、可见性、最小高度。最后调用 refreshDrawableState() 方法,这个是View类的方法,用于更新Drawable状态。   然后再回过头看一下 setChecked (checked)方法,这个用于设置check,也就是button的点击状态。代码如下:
/**     * 

Changes the checked state of this button.

* * @param checked true to check the button, false to uncheck it */ public void setChecked( boolean checked) { if (mChecked != checked) { mChecked = checked; refreshDrawableState(); notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED ); // Avoid infinite recursions if setChecked() is called from a listener if (mBroadcasting ) { return; } mBroadcasting = true ; if (mOnCheckedChangeListener != null) { mOnCheckedChangeListener.onCheckedChanged(this, mChecked); } if (mOnCheckedChangeWidgetListener != null) { mOnCheckedChangeWidgetListener .onCheckedChanged(this, mChecked); } mBroadcasting = false ; } }
  在这个方法中多出了一个接口,这个接口真是check的一个回调接口,代码如下:
/**     * Interface definition for a callback to be invoked when the checked state     * of a compound button changed.     */    public static interface OnCheckedChangeListener {        /**         * Called when the checked state of a compound button has changed.         *         * @param buttonView The compound button view whose state has changed.         * @param isChecked  The new checked state of buttonView.         */        void onCheckedChanged(CompoundButton buttonView, boolean isChecked);    }
  这种回调接口在Android中处处可见,之前的文章也有介绍过。但是在上面的方法,它使用了一个mBroadcasting变量,进而巧妙地避免了重复递归的问题,大家自己感受一下。   然后就是ondraw()方法了,把之前的drawable画出来。代码如下:
  @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        final Drawable buttonDrawable = mButtonDrawable ;        if (buttonDrawable != null) {            final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK ;            final int drawableHeight = buttonDrawable.getIntrinsicHeight();            final int drawableWidth = buttonDrawable.getIntrinsicWidth();            int top = 0;            switch (verticalGravity) {                case Gravity.BOTTOM :                    top = getHeight() - drawableHeight;                    break;                case Gravity.CENTER_VERTICAL :                    top = (getHeight() - drawableHeight) / 2;                    break;            }            int bottom = top + drawableHeight;            int left = isLayoutRtl() ? getWidth() - drawableWidth : 0;            int right = isLayoutRtl() ? getWidth() : drawableWidth;            buttonDrawable.setBounds(left, top, right, bottom);            buttonDrawable.draw(canvas);        }    }
  看得出来,在onDrawable()方法中,最主要的部分还是如何确定上下左右四个参数。确定完后就可以画出来了。但是,CompoundButton是一个抽象类,并不能直接使用,那看一下它的子类是如何实现的:
1、CheckBox
 public class CheckBox extends CompoundButton {    public CheckBox(Context context) {        this(context, null);    }       public CheckBox(Context context, AttributeSet attrs) {        this(context, attrs, com.android.internal.R.attr.checkboxStyle);    }    public CheckBox(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    @Override    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {        super.onInitializeAccessibilityEvent(event);        event.setClassName(CheckBox. class.getName());    }    @Override    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {        super.onInitializeAccessibilityNodeInfo(info);        info.setClassName(CheckBox. class.getName());    }}
  和Button的实现差不多,使用了一个自己的样式。并且也是重写了那两个方法。再来看一下RadioButton,
public class RadioButton extends CompoundButton {       public RadioButton(Context context) {        this(context, null);    }       public RadioButton(Context context, AttributeSet attrs) {        this(context, attrs, com.android.internal.R.attr.radioButtonStyle);    }    public RadioButton(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    /**     * {@inheritDoc}     * 

* If the radio button is already checked, this method will not toggle the radio button. */ @Override public void toggle() { // we override to prevent toggle when the radio is already // checked (as opposed to check boxes widgets) if (!isChecked()) { super.toggle(); } } @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); event.setClassName(RadioButton. class.getName()); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(RadioButton. class.getName()); }}

   和CheckBox实现差不多,区别在于多重写了一个方法,用于防止按钮被重复点击。另外还有ToggleButton以及Switch,前者实现也比较简单,后者稍微麻烦了一些,感兴趣可以自己分析。   最后切入正题,看看滑动Button要如何实现呢?首先看一下效果图:  图1-1     图1-2   图1-1所示的滑动Button实现的思路是这样的,背景图片有开和关的文字,一个按钮在其上面左右滑动,遮住相应的部分,使其在一个位置时候只能看到一个开关。
  如图1-3,在实现的时候,先画一个开关背景图片只,然后在其上面画一个按钮,滑动开关的时候对上面的按钮进行处理即可。   准备:     1、按钮图片        
           2、背景图片                 编码:     在自定义滑动按钮控件的时候,可以有多种选择,可以继承于Button,也可以继承于Button的子类,也可以继承于View类等。我们知道滑动按钮是一个很简单的控件,就是左右滑动改变显示内容,不需要其他的额外东西在里面,所以直接继承于View来实现即可。如果继承于系统的一些控件,那么有很多东西用不到,会造成浪费。     1、定义一个类继承于View,初始化构造方法,在构造方法中加载图片及其信息。     2、重写onMeasure()方法,计算控件的大小。     3、重写onTouchEvent()方法,对滑动事件进行判别处理。     4、定义接口,实现回调。     5、重写onDraw()方法,动态画出按钮。 代码如下:
/****/package com.kince.slidebutton;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;/*** @author kince* @category 左右手势滑动button* @serial 1.0.0* @since 2014.5.17* @see http://blog.csdn.net/wangjinyu501**/public class SlideButton extends View {     private Bitmap slideBitMap;// 滑动图片     private Bitmap switchBitMap;// 背景图片     private int slideBitMapWidth;// 滑动图片宽度     private int switchBitMapWidth;// 背景图片宽度     private int switchBitMapHeight;// 背景图片高度     private boolean currentState;// 开关状态     private boolean isSliding = false; // 是否正在滑动中     private int currentX; // 当前开关的位置     private OnToggleStateChangedListener mChangedListener;// 回调接口     /**     * @param context     *            在java代码中直接调用使用此构造方法     */     public SlideButton(Context context) {          this(context, null);          // TODO Auto-generated constructor stub     }     /**     * @param context     * @param attrs     *            在xml中使用要用到这个方法     */     public SlideButton(Context context, AttributeSet attrs) {          this(context, attrs, 0);          // TODO Auto-generated constructor stub     }     /**     * @param context     * @param attrs     * @param defStyleAttr     *            指定一个样式     */     public SlideButton(Context context, AttributeSet attrs, int defStyleAttr) {          super(context, attrs, defStyleAttr);          initBitmap();     }     /**     * @category 加载背景图片以及开关图片 然后获取各自的宽高     *     */     private void initBitmap() {          // TODO Auto-generated method stub          slideBitMap = BitmapFactory.decodeResource(getResources(),                    R.drawable.slide_button_background);          switchBitMap = BitmapFactory.decodeResource(getResources(),                    R.drawable.switch_background);          slideBitMapWidth = slideBitMap.getWidth();          switchBitMapWidth = switchBitMap.getWidth();          switchBitMapHeight = switchBitMap.getHeight();          Log.i("switchBitMapWidth", switchBitMapWidth + "");     }     @Override     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {          // TODO Auto-generated method stub          super.onMeasure(widthMeasureSpec, heightMeasureSpec);          setMeasuredDimension(switchBitMapWidth, switchBitMapHeight);// 设置控件的宽高     }     @Override     protected void onDraw(Canvas canvas) {          // 绘制button背景图片          canvas.drawBitmap(switchBitMap, 0, 0, null);          // 绘制滑动开关          if (isSliding) {// 如果当前状态是滑动中 则动态绘制开关               int dis = currentX - slideBitMapWidth / 2;               if (dis < 0) {                    dis = 0;               } else if (dis > switchBitMapWidth - slideBitMapWidth) {                    dis = switchBitMapWidth - slideBitMapWidth;               }               canvas.drawBitmap(slideBitMap, dis, 0, null);          } else {               if (currentState) { // 绘制开关为开的状态                    canvas.drawBitmap(slideBitMap, switchBitMapWidth                              - slideBitMapWidth, 0, null);               } else { // 绘制开关为关的状态                    canvas.drawBitmap(slideBitMap, 0, 0, null);               }          }          super.onDraw(canvas);     }     @Override     public boolean onTouchEvent(MotionEvent event) {          // 手势识别 判断滑动方向          int action = event.getAction();          switch (action) {          case MotionEvent.ACTION_DOWN:               isSliding = true;               currentX = (int) event.getX();               break;          case MotionEvent.ACTION_MOVE:               currentX = (int) event.getX();               Log.i("currentX", currentX + "");               break;          case MotionEvent.ACTION_UP:               isSliding = false;               int bgCenter = switchBitMapWidth / 2;               boolean state = currentX > bgCenter; // 改变后的状态               if (state != currentState && mChangedListener != null) {// 添加回调                    mChangedListener.onToggleStateChanged(state);               }               currentState = state;               break;          default:               break;          }          invalidate();          return true;     }     public OnToggleStateChangedListener getmChangedListener() {          return mChangedListener;     }     public void setmChangedListener(               OnToggleStateChangedListener mChangedListener) {          this.mChangedListener = mChangedListener;     }     public boolean isToggleState() {          return currentState;     }     public void setToggleState(boolean currentState) {          this.currentState = currentState;     }}
  回调接口,
  package com.kince.slidebutton;/** * @author kince * */public interface OnToggleStateChangedListener {     /**      * @category      * @param state      */     public void onToggleStateChanged(boolean state);}
  Activity代码,
package com.kince.slidebutton;import android.support.v7.app.ActionBarActivity;import android.support.v7.app.ActionBar;import android.support.v4.app.Fragment;import android.support.v4.app.FragmentActivity;import android.os.Bundle;import android.view.LayoutInflater;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.ViewGroup;import android.widget.Toast;import android.os.Build;public class MainActivity extends ActionBarActivity {     @Override     protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);          if (savedInstanceState == null) {               getSupportFragmentManager().beginTransaction()                         .add(R.id.container, new PlaceholderFragment()).commit();          }     }     @Override     public boolean onCreateOptionsMenu(Menu menu) {          // Inflate the menu; this adds items to the action bar if it is present.          getMenuInflater().inflate(R.menu.main, menu);          return true;     }     @Override     public boolean onOptionsItemSelected(MenuItem item) {          // Handle action bar item clicks here. The action bar will          // automatically handle clicks on the Home/Up button, so long          // as you specify a parent activity in AndroidManifest.xml.          int id = item.getItemId();          if (id == R.id.action_settings) {               return true;          }          return super.onOptionsItemSelected(item);     }     /**     * A placeholder fragment containing a simple view.     */     public static class PlaceholderFragment extends Fragment implements               OnToggleStateChangedListener {          private SlideButton slidebutton;          public PlaceholderFragment() {          }          @Override          public View onCreateView(LayoutInflater inflater, ViewGroup container,                    Bundle savedInstanceState) {               View rootView = inflater.inflate(R.layout.fragment_main, container,                         false);               slidebutton = (SlideButton) rootView.findViewById(R.id.slidebutton1);               // 设置一下开关的状态               slidebutton.setToggleState(true); // 设置开关的状态为打开               slidebutton.setmChangedListener(this);               return rootView;          }          @Override          public void onToggleStateChanged(boolean state) {               // TODO Auto-generated method stub               FragmentActivity activity = getActivity();               if (state) {                    Toast.makeText(activity, "开关打开", 0).show();               } else {                    Toast.makeText(activity, "开关关闭", 0).show();               }          }     }}
  未完待续。


















更多相关文章

  1. Spinner 使用方法小结
  2. 【ClassNotFoundExcept】Android应用程序启动时发生AndroidRunti
  3. Android(安卓)Device Monitor 报 open failed: Permission denie
  4. Android中利用画图类和线程画出闪烁的心形
  5. Android-中常用方法集锦
  6. Android(安卓)首选网络模式默认值的修改方法
  7. android 中如何限制 EditText 最大输入字符数(2)
  8. Android(安卓)Studio 快捷键(转)
  9. android 屏幕保持唤醒 不锁屏

随机推荐

  1. DELPHI10.2的LINUX数据库开发环境配置
  2. linux下安装 tomcat 和配置防火墙开放808
  3. CENTOS 修改MYSQL文件到内存盘
  4. linux查看磁盘使用情况命令
  5. Linux安装开发环境,必须配置的环节(Fedora1
  6. Linux操作以及c编程规范 、附带个问题
  7. Linux 的文件类型
  8. 第2章 计算机工作原理
  9. Linux中断(interrupt)子系统之三:中断流控处
  10. 我应该在哪里添加Yocto位烤任务来创建工