源码:

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>

Android之收音机UI实现(转)的更多相关文章

  1. Android异步更新UI的四种方式

    Android异步更新UI的四种方式 2015-09-06 09:23 segmentfault 字号:T | T 大家都知道由于性能要求,android要求只能在UI线程中更新UI,要想在其他线程中 ...

  2. Android开发之UI更新交互机制与实例解析

    android开发过程中,经常需要更新UI的状态和文案等.这是就需要对UI进行 更新.在android中更新UI一般有三种方法,handler机制.RunOnUiThread方法以及AsyncTask ...

  3. ReactNative Android之原生UI组件动态addView不显示问题解决

    ReactNative Android之原生UI组件动态addView不显示问题解决 版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请表明出处:http://www.cnblogs.com ...

  4. Android开发 ---基本UI组件4:拖动事件、评分进度条、圆圈式进度条、进度条控制

    Android开发 ---基本UI组件4 1.activity_main.xml 描述: 定义了一个按钮 <?xml version="1.0" encoding=" ...

  5. Android开发 ---基本UI组件3:单选按钮、多选按钮、下拉列表、提交按钮、重置按钮、取消按钮

    Android开发 ---基本UI组件2 1.activity_main.xml 描述: 定义一个用户注册按钮 <?xml version="1.0" encoding=&q ...

  6. Android开发 ---基本UI组件2:图像按钮、单选按钮监听、多选按钮监听、开关

    Android开发 ---基本UI组件2 1.activity_main.xml 描述: 定义一个按钮 <?xml version="1.0" encoding=" ...

  7. android线程控制UI更新(Handler 、post()、postDelayed()、postAtTime)

    依照以下的理解就是handler与ui线程有一定的关联能够由于更新界面仅仅能在主线程中全部更新界面的地方能够在接受消息的handleMessage那里还有更新界面能够在handler.port(new ...

  8. Android多线程更新UI的方式

    Android下,对于耗时的操作要放到子线程中,要不然会残生ANR,本次我们就来学习一下Android多线程更新UI的方式. 首先我们来认识一下anr: anr:application not rep ...

  9. Android 线程更新UI报错 : Can't create handler inside thread that has not called Looper.prepare()

    MainActivity中有一个按钮,绑定了save方法 public void save(View view) { String title = titleText.getText().toStri ...

随机推荐

  1. 专题一 Java基础语法

    小辨析: println 输出字符后,下一个输出的字符会换行展示 print 输出字符后,下一个输出字符不会会换展示 system.out.println()  空格 分支结构:if-else使用说明 ...

  2. python-用正则表达式筛选文本信息

    [摘要]  本文主要介绍如何对多个文本进行读取,并采用正则表达式对其中的信息进行筛选,将筛选出来的信息存写到一个新文本. 打开文件:open(‘文件名’,‘打开方式’)>>>file ...

  3. C#面向对象三大特性:多态

    什么是多态 公司最近为了陶冶情操,养了几种动物(Animal),有猫(Cat).狗(Dog).羊(Sheep),这些动物都有共同的特性,会吃(Eat).会叫(Shout),但是它们吃的不同,叫的也不同 ...

  4. 超好用的免费Redis客户端

    Redis这款基于内存的键值对(key-vlaue)数据库,自带了一个基于命令行式的管理工具redis-cli,但是使用起来并不方便.虽然现在有了许多的图形化管理工具,有些需要收费,有些不好用.最终还 ...

  5. 通过颜色绘制图片UIImage

    + (UIImage *)clearImageView { UIColor *color=[UIColor clearColor]; CGRect rect =CGRectMake(,,,); UIG ...

  6. 【C语言】(数组方式)求n名同学的平均成绩

    原理就不说了 代码: #include <stdio.h> int main() { ], sum = ; int i; printf("请输入5名童鞋的成绩:\n") ...

  7. vue项目打包后运行报错400如何解决

    昨天一个Vue项目打包后,今天测试,发现无论localhost还是服务器上都运行不了,报错如下: Failed to load resource: the server responded with ...

  8. acm数论之旅(转载) -- 快速幂

    0和1都不是素数,也不是合数. a的b次方怎么求 pow(a, b)是数学头文件math.h里面有的函数 可是它返回值是double类型,数据有精度误差 那就自己写for循环咯 LL pow(LL a ...

  9. Centos 安装 mysql 5.7

    下载mysql yum包 wget http://repo.mysql.com/mysql57-community-release-el7-10.noarch.rpm 安转软件源 xxx.rpm是刚刚 ...

  10. ubuntu mysql允许root用户远程登录

    有两种方法: 一. 1.mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPT ...