Android之收音机UI实现(转)

源码:

http://www.2cto.com/kf/201211/171417.html

最近在研究收音机的源码,本来想把收音机从源码中提取出来,做成一个单独的应用,但是,收音机需要底层的支持,所以,就有点无能为力了,不过发现UI做得比较有个性(如下图所示),图片比较丑(这是做图片MM的责任哦,哈哈),就单独拿出来跟大家分享一下,顺便自己也学习一下自定义view。

 今天我们主要实现的是中间红色区域的部分,下面我们来看一下它的布局文件radio.xml,它最外围是一个绝对布局,其实我个人是比较反对用绝对布局的,用其他布局也绝对能解决这个问题,大不了可以把图片资源分割一下吗,可见做这个应用的公司做美工的MM是何等的奇缺啊!O(∩_∩)O哈哈~(可怜的程序猿,又少了一个跟MM相处的机会了)。
<?xml version="1.0" encoding="utf-8"?> 
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:background="@drawable/background_main" 
    android:orientation="vertical" > 
 
    <RelativeLayout  www.2cto.com
        android:layout_width="320dip" 
        android:layout_height="113dip" 
        android:layout_x="0dip" 
        android:layout_y="180dip" 
        android:background="@drawable/led_disabled" /> 
 
    <RelativeLayout 
        android:id="@+id/led_background" 
        android:layout_width="320dip" 
        android:layout_height="113dip" 
        android:layout_x="0dip" 
        android:layout_y="180dip" 
        android:background="@drawable/led_enabled" > 
 
        <ImageView 
            android:id="@+id/fm_indicator" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:layout_alignParentLeft="true" 
            android:layout_alignParentTop="true" 
            android:layout_marginLeft="14dip" 
            android:layout_marginTop="12dip" 
            android:src="@drawable/fm" /> 
 
        <ImageView 
            android:id="@+id/headset_indicator" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:layout_marginTop="12dip" 
            android:layout_toRightOf="@id/fm_indicator" 
            android:src="@drawable/headset_indicator" /> 
 
        <TextView 
            android:id="@+id/text_hour_type" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:layout_alignParentRight="true" 
            android:layout_alignParentTop="true" 
            android:layout_marginRight="13dip" 
            android:layout_marginTop="10dip" 
            android:textColor="#000000" /> 
 
        <com.way.view.DigitalTextView 
            android:id="@+id/digital_clock" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:layout_alignParentTop="true" 
            android:layout_marginTop="13dip" 
            android:layout_toLeftOf="@+id/text_hour_type" /> 
 
        <ImageView 
            android:id="@+id/searching_indicator" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:layout_marginTop="13dip" 
            android:layout_toLeftOf="@id/digital_clock" 
            android:src="@drawable/searching" /> 
 
        <com.way.view.DigitalTextView 
            android:id="@+id/digital_freq" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:layout_alignParentRight="true" 
            android:layout_alignParentTop="true" 
            android:layout_marginRight="84dip" 
            android:layout_marginTop="15dip" /> 
 
        <SeekBar 
            android:id="@+id/freq_indicator" 
            android:layout_width="fill_parent" 
            android:layout_height="wrap_content" 
            android:layout_alignParentBottom="true" 
            android:layout_marginBottom="12dip" 
            android:layout_marginLeft="7dip" 
            android:layout_marginRight="7dip" 
            android:progressDrawable="@drawable/blank" 
            android:thumb="@drawable/pointer" 
            android:thumbOffset="0dip" /> 
    </RelativeLayout> 
 
    <FrameLayout 
        android:layout_width="220dip" 
        android:layout_height="30dip" 
        android:layout_x="50dip" 
        android:layout_y="291dip" 
        android:background="@drawable/scroller_bg" > 
 
        <com.way.view.TuneWheel 
            android:id="@+id/tune_wheel" 
            android:layout_width="fill_parent" 
            android:layout_height="fill_parent" /> 
 
        <ImageView 
            android:layout_width="fill_parent" 
            android:layout_height="fill_parent" 
            android:src="@drawable/tune_wheel_highlight" /> 
    </FrameLayout> 
 
    <ImageButton 
        android:id="@+id/add_button" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_x="39dip" 
        android:layout_y="332dip" 
        android:background="#00000000" 
        android:src="@drawable/add_button" /> 
 
    <ImageButton 
        android:id="@+id/prev_station_button" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_x="108dip" 
        android:layout_y="332dip" 
        android:background="#00000000" 
        android:src="@drawable/prev_station_button" /> 
 
    <ImageButton 
        android:id="@+id/search_prev_button" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_x="176dip" 
        android:layout_y="332dip" 
        android:background="#00000000" 
        android:src="@drawable/search_prev_button" /> 
 
    <com.way.view.CheckableImageButton 
        android:id="@+id/headset_toggle" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_x="255dip" 
        android:layout_y="332dip" 
        android:background="#00000000" /> 
 
    <com.way.view.CheckableImageButton 
        android:id="@+id/power_toggle" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_x="0dip" 
        android:layout_y="403dip" 
        android:background="#00000000" /> 
 
    <ImageButton 
        android:id="@+id/radio_list_button" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_x="80dip" 
        android:layout_y="402dip" 
        android:background="#00000000" 
        android:src="@drawable/radio_list_button" /> 
 
    <ImageButton 
        android:id="@+id/next_station_button" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_x="146dip" 
        android:layout_y="404dip" 
        android:background="#00000000" 
        android:src="@drawable/next_station_button" /> 
 
    <ImageButton 
        android:id="@+id/search_next_button" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_x="215dip" 
        android:layout_y="402dip" 
        android:background="#00000000" 
        android:src="@drawable/search_next_button" /> 
 
</AbsoluteLayout> 

接下来让我们来看一下那个自定义滚轮的代码TuneWheel:
/**
 * 调频的滚轮
 * 
 * @author way
 * 
 */ 
public class TuneWheel extends View { 
    /**
     * 滚轮滚动监听接口
     */ 
    public interface OnTuneWheelValueChangedListener { 
        public void onTuneWheelValueChanged(View v, float changedBy); 
    } 
 
    private static final String TAG = "TuneWheel"; 
    private static final int LINE_SPAN = 8;//线性跨度 
    private static final int DRAG_THRESHOLD = 5;//滚动的最小阈值,即在屏幕x轴上至少滑动5个坐标点才有效 
    private static final float CHANGE_THRESHOLD = 0.1f;//最小的改变阈值,即需要传递出去的那个值的最小值 
    public static final int DIRECTION_PREV = 1;//向左滚动的方向值 
    public static final int DIRECTION_NEXT = 2;//向右滚动的方向值 
 
    private boolean mIsMoving = false;// 是否在滑动 
    private float mLastDragPos;//最终的滚动位置 
    private float mCurrentPos = 0;// 当前位置 
    private float mCurrentChange = 0;// 当前改变值 
    private int mDragEnable = DIRECTION_PREV | DIRECTION_NEXT; 
 
    private OnTuneWheelValueChangedListener mListener = null;// 监听接口对象 
 
    public void setOnValueChangedListener( 
            OnTuneWheelValueChangedListener listener) {// 提供监听方法 
        mListener = listener; 
    } 
 
    /**
     * 根据传入的方向设置是否允许滚动
     * 
     * @param direction
     *            传入的方向
     * @param enable
     *            是否能滚动
     */ 
    public void setDragEnable(int direction, boolean enable) { 
        if (enable) { 
            mDragEnable = mDragEnable | direction; 
        } else { 
            mDragEnable = mDragEnable ^ direction; 
        } 
    } 
 
    /**
     * 根据输入的方向判断是否允许滚动
     * 
     * @param direction
     *            传入的方向
     * @return 是否能滚动
     */ 
    public boolean getDragEnable(int direction) { 
        return (mDragEnable & direction) != 0; 
    } 
 
    // 三个构造器 
    public TuneWheel(Context context) { 
        super(context); 
    } 
 
    public TuneWheel(Context context, AttributeSet attrs) { 
        super(context, attrs); 
    } 
 
    public TuneWheel(Context context, AttributeSet attrs, int defStyle) { 
        super(context, attrs, defStyle); 
    } 
 
    // 重写onDraw方法 
    protected void onDraw(Canvas canvas) { 
        Paint mLinePaint = new Paint(); 
        mLinePaint.setARGB(0xEC, 0x00, 0, 0); 
        mLinePaint.setStrokeWidth(0.5f); 
        for (int pos = 0; pos + mCurrentPos < getWidth(); pos += LINE_SPAN) { 
            canvas.drawLine(pos + mCurrentPos, 0, pos + mCurrentPos, 
                    getHeight(), mLinePaint); 
        } 
    } 
 
    // 重写onTouchEvent方法,实现滚动效果 
    public boolean onTouchEvent(MotionEvent event) { 
        if (!this.isEnabled()) 
            return false; 
 
        int action = event.getAction(); 
 
        switch (action) { 
        case MotionEvent.ACTION_DOWN://按下 
            mLastDragPos = event.getX();//获取按下时的x轴坐标值,滚轮只能左右滑动,所以只要获取x值即可 
            break; 
        case MotionEvent.ACTION_MOVE://移动 
            float currentDragPos = event.getX(); 
 
            if (!getDragEnable(getDirection(currentDragPos, mLastDragPos)))//如果当前方向不允许滚动 
                return false; 
 
            if (!mIsMoving) {//如果当前状态为没有滚动 
                if (Math.abs(currentDragPos - mLastDragPos) > DRAG_THRESHOLD) {//如果滚动范围大于最小阈值 
                    mIsMoving = true; 
                    mLastDragPos = currentDragPos; 
                } else { 
                    return false; 
                } 
            } else {//如果当前状态为一直在滚动 
                float tempPos = (mCurrentPos + currentDragPos - mLastDragPos) 
                        % LINE_SPAN;//保存一个临时变化值 
 
                mCurrentChange += calculateChange(currentDragPos, mLastDragPos); 
                if (Math.abs(mCurrentChange) > CHANGE_THRESHOLD) { 
                    if (mListener != null) { 
                        mListener.onTuneWheelValueChanged(this, mCurrentChange);//将滚动变化值通过监听接口传递出去 
                    } 
                    mCurrentChange = 0; 
                } 
 
                mCurrentPos = tempPos; 
                invalidate();//千万要记得刷新一下界面 
                mLastDragPos = currentDragPos; 
            } 
            break; 
        case MotionEvent.ACTION_UP://松开 
            if (mIsMoving) { 
                mIsMoving = false;//松开后更新状态为停止滚动0. 
            } 
            break; 
        } 
 
        return true; 
    } 
 
    /**
     * 根据改变计算滚动方向
     * 
     * @param current
     *            当前位置值
     * @param last
     *            最终位置值
     * @return 方向值
     */ 
    private int getDirection(float current, float last) { 
        return (int) (Math.signum(current - last) + 3) / 2; 
    } 
 
    /**
     * 根据改变计算改变值
     * 
     * @param current
     *            当前位置值
     * @param last
     *            最终位置值
     * @return 改变值
     */ 
    private float calculateChange(float current, float last) { 
        float sub = current - last; 
        return Math.signum(sub) * (float) Math.pow(Math.abs(sub), 1.3) / 100; 
    } 


第三,是中间那个显示时间和调频自定义的View,DigitalTextView.java:
/**
 * 自定义的一个显示调频或时间的view
 * 
 * @author way
 * 
 */ 
public class DigitalTextView extends LinearLayout { 
 
    private static final String TAG = "DigitalTextView"; 
    private String mResourcePrefix = "";// 资源前缀,时间或调频 
 
    // 两个构造器,自定义view中至少要有2个构造器 
    public DigitalTextView(Context context) { 
        super(context); 
        init(); 
    } 
 
    public DigitalTextView(Context context, AttributeSet attrs) { 
        super(context, attrs); 
        init(); 
    } 
 
    // 初始化 
    private void init() { 
        this.setOrientation(LinearLayout.HORIZONTAL); 
    } 
 
    /**
     * 获取对应的时间图片
     * 
     * @param index
     *            时间值
     * @return 对应的时间图片id
     */ 
    private int getTimeDrawable(int index) { 
        int drawableId = -1; 
        switch (index) { 
        case 0: 
            drawableId = R.drawable.time_0; 
            break; 
        case 1: 
            drawableId = R.drawable.time_1; 
            break; 
        case 2: 
            drawableId = R.drawable.time_2; 
            break; 
        case 3: 
            drawableId = R.drawable.time_3; 
            break; 
        case 4: 
            drawableId = R.drawable.time_4; 
            break; 
        case 5: 
            drawableId = R.drawable.time_5; 
            break; 
        case 6: 
            drawableId = R.drawable.time_6; 
            break; 
        case 7: 
            drawableId = R.drawable.time_7; 
            break; 
        case 8: 
            drawableId = R.drawable.time_8; 
            break; 
        case 9: 
            drawableId = R.drawable.time_9; 
            break; 
        } 
        return drawableId; 
    } 
 
    /**
     * 获取调频图片
     * 
     * @param index
     *            频率值
     * @return 对应频率值的图片id
     */ 
    private int getFreqDrawable(int index) { 
        int drawableId = -1; 
        switch (index) { 
        case 0: 
            drawableId = R.drawable.freq_0; 
            break; 
        case 1: 
            drawableId = R.drawable.freq_1; 
            break; 
        case 2: 
            drawableId = R.drawable.freq_2; 
            break; 
        case 3: 
            drawableId = R.drawable.freq_3; 
            break; 
        case 4: 
            drawableId = R.drawable.freq_4; 
            break; 
        case 5: 
            drawableId = R.drawable.freq_5; 
            break; 
        case 6: 
            drawableId = R.drawable.freq_6; 
            break; 
        case 7: 
            drawableId = R.drawable.freq_7; 
            break; 
        case 8: 
            drawableId = R.drawable.freq_8; 
            break; 
        case 9: 
            drawableId = R.drawable.freq_9; 
            break; 
        } 
        return drawableId; 
    } 
 
    /**
     * 根据传递进来的字符,返回对应的图片资源
     * 
     * @param c
     *            传递进来的字符
     * @return 对应的图片id
     */ 
    private int getResourceForChar(char c) { 
        if (c == '.') { 
            if (!mResourcePrefix.equals("time")) { 
                return R.drawable.freq_dot; 
            } 
        } else if (c == ':') { 
            if (mResourcePrefix.equals("time")) { 
                return R.drawable.time_colon; 
            } 
        } else if (c >= '0' && c <= '9') { 
            if (mResourcePrefix.equals("time")) { 
                return getTimeDrawable(c - '0'); 
            } else { 
                return getFreqDrawable(c - '0'); 
            } 
        } else { 
            return -1; 
        } 
        return -1; 
    } 
 
    // 创建一个ImageView 
    private ImageView createImageView() { 
        ImageView imageView = new ImageView(getContext()); 
        LayoutParams param = new LayoutParams( 
                LinearLayout.LayoutParams.WRAP_CONTENT, 
                LinearLayout.LayoutParams.WRAP_CONTENT); 
        imageView.setLayoutParams(param); 
        return imageView; 
    } 
 
    /**
     * 设置资源前缀,是时间还是调频
     * 
     * @param resourcePrefix
     */ 
    public void setResourcePrefix(String resourcePrefix) { 
        mResourcePrefix = resourcePrefix; 
    } 
 
    /**
     * 设置数字值,类似TextView的setText()
     * 
     * @param text
     *            传递进来的字符串
     */ 
    public void setDigitalText(String text) { 
        updateView(text); 
    } 
 
    /**
     * 更新自定义TextView
     * 
     * @param text
     *            传递进来的字符串
     */ 
    private void updateView(String text) { 
 
        int startIndex = getChildCount() - text.length();// 起始位置,因为imageView的数量是根据字符串的长度创建的 
        if (startIndex < 0)//第一次更新的时候肯定是小于0的 
            startIndex = 0; 
 
        for (int i = 0; i < startIndex; i++) { 
            getChildAt(i).setVisibility(View.GONE);//把之前的图片隐藏起来,个人感觉这样做得不怎么样 
        } 
 
        //下面是根据字符串的长度,循环更换为对应的图片 
        for (int i = 0; i < text.length(); i++) { 
            int childId = i + startIndex; 
            int resId = getResourceForChar(text.charAt(i));//将每个字符转换为数字 
 
            if (resId != -1) { 
                if (childId == getChildCount()) { 
                    addView(createImageView());//添加到LinearLayout中 
                } 
                ImageView child = ((ImageView) getChildAt(childId)); 
                child.setVisibility(View.VISIBLE); 
                child.setImageResource(resId); 
            } 
        } 
    } 

最后,就是把这些自定义View综合起来的MainActivity了:
public class Main extends Activity implements OnTuneWheelValueChangedListener { 
    private static final int FREQ_POINTER_SCALE = 10; 
    private TuneWheel mTuneWheel;// 滚轮 
    private DigitalTextView mFreqView;// 显示调频波段的view 
    private SeekBar mFreqPointer;// 进度条指针 
    private TextView mClockType;// 显示时间的格式pm、am 
    private DigitalTextView mDigitalClock;// 显示时间的view 
    private String mClockTypeString;// 时间 
    private View mSearchingIndicator;// 加载时间的时候刷新图标 
    private CheckableImageButton mPowerToggle, mHeadsetToggle;// 开关键和耳机扬声器切换键 
    private DigitalClockUpdater mDigitalClockUpdater;// 更新时间的对象 
    private float channel = 87.5f;// 默认初始的波段 
 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.radio); 
        initView();// 初始化view 
    } 
 
    private void initView() { 
        // 开关键 
        mPowerToggle = (CheckableImageButton) findViewById(R.id.power_toggle); 
        mPowerToggle.setDrawable(R.drawable.power_button_checked, 
                R.drawable.power_button_unchecked, 
                R.drawable.power_button_disabled); 
        // 耳机扬声器切换键 
        mHeadsetToggle = (CheckableImageButton) findViewById(R.id.headset_toggle); 
        mHeadsetToggle.setDrawable(R.drawable.headset_button_checked, 
                R.drawable.headset_button_unchecked, 
                R.drawable.headset_button_disabled); 
 
        mClockType = (TextView) findViewById(R.id.text_hour_type);// 时间类型 
        Date curDate = new Date(java.lang.System.currentTimeMillis()); 
        int hours = curDate.getHours(); 
        if (!DateFormat.is24HourFormat(Main.this)) {// 如果不是24小时制 
            if (hours > 12) { 
                mClockTypeString = getString(R.string.pm); 
            } else { 
                mClockTypeString = getString(R.string.am); 
            } 
            mClockType.setText(mClockTypeString);// 设置pm、am格式 
        } else { 
            mClockType.setText("");// 如果所24小时制,就不要任何标识 
        } 
        mDigitalClock = (DigitalTextView) findViewById(R.id.digital_clock); 
        mDigitalClock.setResourcePrefix("time"); 
        mDigitalClockUpdater = new DigitalClockUpdater(mDigitalClock); 
        mDigitalClockUpdater.run();// 启动更新时间 
 
        mTuneWheel = (TuneWheel) findViewById(R.id.tune_wheel);// 滚轮 
        mTuneWheel.setOnValueChangedListener(this);// 监听滚动事件 
 
        // 显示波段的view 
        mFreqView = (DigitalTextView) findViewById(R.id.digital_freq); 
        mFreqView.setResourcePrefix("freq"); 
        mFreqView.setDigitalText(String.valueOf(channel));// 初始化为默认值,实际从配置文件中读取上次保存的频段 
 
        mSearchingIndicator = findViewById(R.id.searching_indicator); 
        mSearchingIndicator.setVisibility(View.INVISIBLE);// 加载完时间后,自动隐藏这个图标 
 
        // 频段进度条 
        mFreqPointer = (SeekBar) findViewById(R.id.freq_indicator); 
        // 设置进度条范围 
        mFreqPointer 
                .setMax((int) ((WheelConfig.RADIO_MAX_FREQUENCY - WheelConfig.RADIO_MIN_FREQUENCY) * FREQ_POINTER_SCALE)); 
        mFreqPointer.setEnabled(false); 
    } 
 
    @Override 
    public void onTuneWheelValueChanged(View v, float changedBy) {// 滚轮滑动监听事件 
        // TODO Auto-generated method stub 
        float freq = adjustFreq(WheelConfig.format(channel + changedBy));// 调整滚轮滑动变化值 
        channel = freq;// 用一个全局变量保存一下当前的调频 
        setFreqForUi(freq);// 更新ui 
    } 
 
    /**
     * 调整和限制滚轮滑动的值
     * 
     * @param freq
     *            需要调整的值
     * @return 调整后的值
     */ 
    private float adjustFreq(float freq) { 
        float result = ((int) (freq * 10)) / 10f; 
        if (result < WheelConfig.RADIO_MIN_FREQUENCY) 
            result = WheelConfig.RADIO_MIN_FREQUENCY; 
 
        if (result > WheelConfig.RADIO_MAX_FREQUENCY) 
            result = WheelConfig.RADIO_MAX_FREQUENCY; 
        return result; 
    } 
 
    /**
     * 通过传入的滚轮值,更新ui
     * 
     * @param freq
     *            调整后的滚轮值
     */ 
    private void setFreqForUi(float freq) { 
        if (freq == WheelConfig.RADIO_MIN_FREQUENCY) { 
            mTuneWheel.setDragEnable(TuneWheel.DIRECTION_PREV, false); 
            mTuneWheel.setDragEnable(TuneWheel.DIRECTION_NEXT, true); 
        } else if (freq == WheelConfig.RADIO_MAX_FREQUENCY) { 
            mTuneWheel.setDragEnable(TuneWheel.DIRECTION_PREV, true); 
            mTuneWheel.setDragEnable(TuneWheel.DIRECTION_NEXT, false); 
        } else { 
            mTuneWheel.setDragEnable(TuneWheel.DIRECTION_PREV, true); 
            mTuneWheel.setDragEnable(TuneWheel.DIRECTION_NEXT, true); 
        } 
        // 更新进度条位置 
        mFreqPointer 
                .setProgress((int) ((freq - WheelConfig.RADIO_MIN_FREQUENCY) * FREQ_POINTER_SCALE)); 
        mFreqView.setDigitalText(String.valueOf(freq));// 更新当前频段值 
    } 
 
    @Override 
    protected void onDestroy() { 
        super.onDestroy(); 
        mDigitalClockUpdater.stop();// 停止更新时间 
    } 
 
    /**
     * 更新时间的类
     * 
     * @author way
     * 
     */ 
    private class DigitalClockUpdater { 
        private static final int MSG_UPDATE_TIME = 0x001; 
        private static final int UPDATE_INTERNAL = 1000; 
        DigitalTextView mView = null; 
 
        boolean mRunning = false; 
 
        Handler mUpdateHandler = new Handler() { 
            @Override 
            public void handleMessage(Message msg) { 
                if (msg.what == MSG_UPDATE_TIME) { 
                    Date curDate = new Date( 
                            java.lang.System.currentTimeMillis()); 
                    int hours = curDate.getHours(); 
                    int minutes = curDate.getMinutes(); 
 
                    if (DateFormat.is24HourFormat(Main.this)) { 
                        if (minutes < 10) { 
                            mView.setDigitalText("" + hours + ":0" + minutes); 
                        } else { 
                            mView.setDigitalText("" + hours + ":" + minutes); 
                        } 
                        mClockTypeString = ""; 
                    } else { 
                        hours = hours > 12 ? hours - 12 : hours; 
                        if (minutes < 10) { 
                            mView.setDigitalText("" + hours + ":0" + minutes); 
                        } else { 
                            mView.setDigitalText("" + hours + ":" + minutes); 
                        } 
                    } 
 
                    if (mRunning) 
                        this.sendEmptyMessageDelayed(MSG_UPDATE_TIME, 
                                UPDATE_INTERNAL);// 每隔一秒就更新一下时间,保持与系统同步,其实如果只需要每分钟更新时间,可以通过接收系统广播同步,这个广播每分钟发送一次。 
                } 
            } 
        }; 
 
        public DigitalClockUpdater(DigitalTextView view) { 
            mView = view; 
        } 
 
        public void run() { 
            mRunning = true; 
            mUpdateHandler.removeMessages(MSG_UPDATE_TIME); 
            mUpdateHandler.sendEmptyMessage(MSG_UPDATE_TIME); 
        } 
 
        public void stop() { 
            mRunning = false; 
            mView = null; 
            mUpdateHandler.removeMessages(MSG_UPDATE_TIME); 
        } 
    } 
}<span style="font-size:18px;"> 
</span> 

原文地址:https://www.cnblogs.com/mochaMM/p/5151109.html