源码:

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. IDF-CTF-简单的Elf逆向Writeup

    ElfCrackMe1 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !imp ...

  2. 题解 CF546B Soldier and Badges

    CF546B Soldier and Badges 简单的贪心qwq 排个序,如果当前数与之前的数相重,已经用过,则加到一个之前没有用过的数 #include<cstdio> #inclu ...

  3. Spring Cloud Alibaba 实战 之 Nacos 服务注册和发现

    服务注册与发现,服务发现主要用于实现各个微服务实例的自动化注册与发现,是微服务治理的核心,学习 Spring Cloud Alibaba,首先要了解框架中的服务注册和发现组件——Nacos. 一.Sp ...

  4. Vue - 实现双击显示编辑框;自动聚焦点击的显示框;点击编辑框外的地方,隐藏编辑框

    实现这三个功能的踩坑记录. 1. 需求 在Vue中,有一个input, 双击时编辑文本,点击该input节点外的其他地方,则取消编辑. 那么这里有三个要实现的地方 第一是双击显示编辑框. 第二是自动聚 ...

  5. map遍历删除

    List<Object> orderManageList = cacheService.values(key); Iterator<Object> it=orderManage ...

  6. js 根据data-i 降序排列

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. 吴裕雄 python 神经网络——TensorFlow图片预处理

    import numpy as np import tensorflow as tf import matplotlib.pyplot as plt # 使用'r'会出错,无法解码,只能以2进制形式读 ...

  8. 【MySQL】数据类型之字符相关

    " 目录 字符类型 char类型 varchar类型 实测 总结 枚举类型与集合类型 字符类型 官网:https://dev.mysql.com/doc/refman/5.7/en/char ...

  9. JavaSE复习~Java语言发展史

    Java概述 什么是Java语言? Java语言是美国Sun公司(Stanford University NetWork),在1995年推出的高级编程语言. 所谓编程语言,是计算机的语言,人们可以使用 ...

  10. Linux 常用命令——解压缩文件

    tar 解包:tar xvf FileName.tar打包:tar cvf FileName.tar DirName(注:tar是打包,不是压缩!)———————————————.gz解压1:gunz ...