Chinaunix首页 | 论坛 | 博客
  • 博客访问: 586151
  • 博文数量: 192
  • 博客积分: 3780
  • 博客等级: 中校
  • 技术积分: 1487
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-26 10:11
文章存档

2012年(6)

2011年(160)

2010年(26)

分类: Java

2011-11-10 10:57:45

48.Android控件开发之ToggleButton原理

在Android平台上比较有特色的就是 ToggleButton控件,虽然它的功能和CheckBox有些类似,但是他们的用处还是有一定的区别比如ToggleButton原本有图片装饰, 通过ToggleButton可以很清楚的显示某些状态。它们均从Button为基类的CompoundButton中实现,其真假事件从 Checkable来实现。

  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(); //显式的GC
    }

    public void toggle() {
        setChecked(!mChecked);
    }

    @Override
    public boolean performClick() {
              toggle();
        return super.performClick();
    }

    public boolean isChecked() {
        return mChecked;
    }

    public void setChecked(boolean checked) {
        if (mChecked != checked) {
            mChecked = checked;
            refreshDrawableState(); //更新当前状态的按钮图标

            if (mBroadcasting) {
                return;
            }

            mBroadcasting = true;
            if (mOnCheckedChangeListener != null) {
                mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
            }
            if (mOnCheckedChangeWidgetListener != null) {
                mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
            }

            mBroadcasting = false;           
        }
    }

    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        mOnCheckedChangeListener = listener;
    }

    void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) {
        mOnCheckedChangeWidgetListener = listener;
    }

    public static interface OnCheckedChangeListener {
        void onCheckedChanged(CompoundButton buttonView, boolean isChecked);
    }

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

    public void setButtonDrawable(Drawable d) {
        if (d != null) {
            if (mButtonDrawable != null) {
                mButtonDrawable.setCallback(null);
                unscheduleDrawable(mButtonDrawable);
            }
            d.setCallback(this);
            d.setState(getDrawableState());
            d.setVisible(getVisibility() == VISIBLE, false);
            mButtonDrawable = d;
            mButtonDrawable.setState(null);
            setMinHeight(mButtonDrawable.getIntrinsicHeight());
        }

        refreshDrawableState();
    }

    @Override
    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
        boolean populated = super.dispatchPopulateAccessibilityEvent(event);

        if (!populated) {
            int resourceId = 0;
            if (mChecked) {
                resourceId = R.string.accessibility_compound_button_selected;
            } else {
                resourceId = R.string.accessibility_compound_button_unselected;
            }
            String state = getResources().getString(resourceId);
            event.getText().add(state);
            event.setChecked(mChecked);
        }

        return populated;
    }

    @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 height = buttonDrawable.getIntrinsicHeight();

            int y = 0;

            switch (verticalGravity) {
                case Gravity.BOTTOM:
                    y = getHeight() - height;
                    break;
                case Gravity.CENTER_VERTICAL:
                    y = (getHeight() - height) / 2;
                    break;
            }

            buttonDrawable.setBounds(0, y, buttonDrawable.getIntrinsicWidth(), y + height);
            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() { //android123提示状态改变时需要更换按钮的图标
        super.drawableStateChanged();
        if (mButtonDrawable != null) {
            int[] myDrawableState = getDrawableState();
            mButtonDrawable.setState(myDrawableState);
            invalidate();
        }
    }

    @Override
    protected boolean verifyDrawable(Drawable who) {
        return super.verifyDrawable(who) || who == mButtonDrawable;
    }

    static class SavedState extends BaseSavedState {
        boolean checked;

             SavedState(Parcelable superState) {
            super(superState);
        }
        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();
    }
}

从上面来看我们知道CompuundButton的实现相对繁琐了些,主要是考虑状态是否已经选中等情况的消息通知,Android开发网提醒大家而ToggleButton相对CompuundButton增加的给用户而言主要是开关的文字显示。

public class ToggleButton extends CompoundButton {
    private CharSequence mTextOn;
    private CharSequence mTextOff;
    private Drawable mIndicatorDrawable;

    private static final int NO_ALPHA = 0xFF;
    private float mDisabledAlpha;
    public ToggleButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a =
            context.obtainStyledAttributes(
                    attrs, com.android.internal.R.styleable.ToggleButton, defStyle, 0);
        mTextOn = a.getText(com.android.internal.R.styleable.ToggleButton_textOn);
        mTextOff = a.getText(com.android.internal.R.styleable.ToggleButton_textOff);
        mDisabledAlpha = a.getFloat(com.android.internal.R.styleable.ToggleButton_disabledAlpha, 0.5f);
        syncTextState();
        a.recycle();
    }

    public ToggleButton(Context context, AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.buttonStyleToggle);
    }

    public ToggleButton(Context context) {
        this(context, null);
    }

    @Override
    public void setChecked(boolean checked) {
        super.setChecked(checked);
        syncTextState();
    }

    private void syncTextState() {
        boolean checked = isChecked();
        if (checked && mTextOn != null) {
            setText(mTextOn);
        } else if (!checked && mTextOff != null) {
            setText(mTextOff);
        }
    }

    public CharSequence getTextOn() {
        return mTextOn;
    }

    public void setTextOn(CharSequence textOn) {
        mTextOn = textOn;
    }

    public CharSequence getTextOff() {
        return mTextOff;
    }

    protected void onFinishInflate() {
        super.onFinishInflate();
        updateReferenceToIndicatorDrawable(getBackground());
    }

    @Override
    public void setBackgroundDrawable(Drawable d) {
        super.setBackgroundDrawable(d);
        updateReferenceToIndicatorDrawable(d);
    }

    private void updateReferenceToIndicatorDrawable(Drawable backgroundDrawable) {
        if (backgroundDrawable instanceof LayerDrawable) {
            LayerDrawable layerDrawable = (LayerDrawable) backgroundDrawable;
            mIndicatorDrawable =
                    layerDrawable.findDrawableByLayerId(com.android.internal.R.id.toggle);
        }
    }
    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        if (mIndicatorDrawable != null) {
            mIndicatorDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
        }
    }
}

49. AsyncTask实例代码演示Android异步任务

上 次我们讲到了Android提供了一个较线程更简单的处理多任务的方法AsyncTask异步任务类,相对于线程来说AsyncTask对于简单的任务处 理更安全,其内部的实现方法使用了Android的Handler机制,对于常见的文件下载可以使用AsyncTask类来处理,在Browser浏览器 中就是用了该类下载Web服务器URL的Favicon图标。

  首先Android123以简单的下载例子演示该类的大致结构,如下

private class DownloadFilesTask extends AsyncTask {
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count)100));
        }
        return totalSize;
    }
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }
    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}

  最终我们执行 DownloadFilesTask().execute(url1, url2, url3); 即可。

  在Android浏览器中下载Favicon图标的实现如下:

class DownloadTouchIcon extends AsyncTask {
    private final ContentResolver mContentResolver;
    private final Cursor mCursor;
    private final String mOriginalUrl;
    private final String mUrl;
    private final String mUserAgent;
    /* package */ BrowserActivity mActivity;

    public DownloadTouchIcon(BrowserActivity activity, ContentResolver cr,
            Cursor c, WebView view) { //构造方法
        mActivity = activity;
        mContentResolver = cr;
        mCursor = c;
        mOriginalUrl = view.getOriginalUrl();
        mUrl = view.getUrl();
        mUserAgent = view.getSettings().getUserAgentString();
    }

    public DownloadTouchIcon(ContentResolver cr, Cursor c, String url) { //实现本类的构造
        mActivity = null;
        mContentResolver = cr;
        mCursor = c;
        mOriginalUrl = null;
        mUrl = url;
        mUserAgent = null;
    }

    @Override
    public Bitmap doInBackground(String... values) {   //返回Bitmap类型
        String url = values[0];

        AndroidHttpClient client = AndroidHttpClient.newInstance(mUserAgent);
        HttpGet request = new HttpGet(url);

       HttpClientParams.setRedirecting(client.getParams(), true); //处理302等重定向问题

        try {
            HttpResponse response = client.execute(request);

            if (response.getStatusLine().getStatusCode() == 200) { //如果OK
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    InputStream content = entity.getContent(); //将图标保存到InputStream中,因为是二进制内容
                    if (content != null) {
                        Bitmap icon = BitmapFactory.decodeStream( //从流中取出Bitmap,这里使用了BitmapFactory类的静态方法decodeStream
                                content, null, null);
                        return icon;
                    }
                }
            }
        } catch (IllegalArgumentException ex) {
            request.abort();
        } catch (IOException ex) {
            request.abort();
        } finally {
            client.close();
        }
        return null;
    }

    @Override
    protected void onCancelled() {
        if (mCursor != null) {
            mCursor.close();
        }
    }

    @Override
    public void onPostExecute(Bitmap icon) {
          if (mActivity != null) {
             mActivity.mTouchIconLoader = null;
        }

        if (icon == null || mCursor == null || isCancelled()) {
            return;
        }

    最终图标要保存到浏览器的内部数据库中,系统程序均保存为SQLite格式,Browser也不例外,因为图片是二进制的所以使用字节数组存储数据库的BLOB类型

        final ByteArrayOutputStream os = new ByteArrayOutputStream();
        icon.compress(Bitmap.CompressFormat.PNG, 100, os); //将Bitmap压缩成PNG编码,质量为100%存储
        ContentValues values = new ContentValues(); //构造SQLite的Content对象,这里也可以使用raw sql代替
        values.put(Browser.BookmarkColumns.TOUCH_ICON,os.toByteArray()); //写入数据库的Browser.BookmarkColumns.TOUCH_ICON字段

        if (mCursor.moveToFirst()) {
            do {
                mContentResolver.update(ContentUris.withAppendedId(Browser.BOOKMARKS_URI, mCursor.getInt(0)),values, null, null);
            } while (mCursor.moveToNext());
        }
        mCursor.close();
    }
}

  本次Android开发网通过两个AsyncTask类演示了多种类型的任务构造,这里大家注意返回类型,本节演示了Android平台上Content Provider、AsyncTask、Bitmap、HTTP以及Stream的相关操作,大家如何想很快提高开发水平其实只要理解Google如何去 实现Android系统常规构架就可以轻松入门谷歌移动平台。


http://hi.baidu.com/286177943/blog/item/d21b60fd4a77fbe7fc037fba.html

阅读(1512) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~