源码:

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

Android之收音机UI实现(转)相关推荐

  1. android 界面组件,安卓开发学习周第三篇——Android中的UI组件

    原标题:安卓开发学习周第三篇--Android中的UI组件 在Android APP中,所有的用户界面元素都是由View和ViewGroup的对象构成的.View是绘制在屏幕上的用户能与之交互的一个对 ...

  2. 谷歌正式发布Android 12,UI更好看,应用更快,打造独属于自己的定制化属性

    焕然一新的Android 12 今年 5 月的 Google I/O 大会上,谷歌推出了 Android 12 系统,这是原生安卓系统史上最大的设计变化,从此旧貌换新颜. 不只是外观,Android ...

  3. android隐藏系统ui_如何启用Android的“系统UI调谐器”以访问实验功能

    android隐藏系统ui Starting with Marshmallow, Google included a new hidden settings menu with experimenta ...

  4. android隐藏系统ui_如何在非库存设备上获取Android的系统UI调谐器

    android隐藏系统ui One benefit users of stock Android devices have had for the past few iterations of the ...

  5. 三星s10升级android11,外媒:三星S10已开始推送基于Android 11的UI 3.0更新

    [手机中国新闻]据外媒消息,三星S10已开始推送基于Android 11的UI 3.0更新,不过它花费的时间可能比用户所希望的要长一点. 三星S10 正如外媒SamMobile指出的那样,此次更新正在 ...

  6. Android开发自定义UI组件

    Android开发自定义UI组件实现红色小球跟随手指移动 要写实现自定义UI组件,要创建一个BallView类,继承View类,在BallView类中创建画笔,然后重写OnDraw()方法和OnTou ...

  7. Android阿拉伯语UI适配问题汇总

    Android阿拉伯语UI适配问题汇总 Sensi Sun RTL和LTR LTR 是 Left-to-right(从左向右) 的缩写.(常用) RTL 是 Right-to-left(从右向左) 的 ...

  8. 基于android 10的国产手机,基于Android的国产手机UI比拼:ColorOS、MIUI、EMUI,你打算盘谁...

    原标题:基于Android的国产手机UI比拼:ColorOS.MIUI.EMUI,你打算盘谁 可能很多人都有一种手机系统使用依赖性.惰性,用惯了年轻时尚体验感好的ColorOS不愿意去尝试刻板的EMU ...

  9. Android平台手机UI应用开发——软件管理器

    第一次写博客,与大家分享源代码,博客很多地方不知道怎么设计,望见谅. 希望大家多多支持,雅林和大家一起学习Android,共同进步. 今天我们要做的这个软件管理器,主要目的是为了学习Android平台 ...

最新文章

  1. linux运算_LinuxShell算术运算
  2. ubuntu下文件名乱码的解决办法
  3. SpringMVC + Apache POI 实现WEB中Excel下载功能
  4. C/C++学习之路_六: 指针
  5. Spring Boot JPA中使用@Entity和@Table
  6. codesys 串口通讯实例_常见的PLC程序实例,车库自动门的PLC控制!
  7. Golang基本数据类型的相互转换
  8. 索引超出了数组界限_【每天一题】LeetCode 26. 删除排序数组中的重复项
  9. 计算机逻辑学包含分析,逻辑学在职研究生教育的基本内容分析
  10. 【Matlab学习笔记】【数据类型】介绍
  11. 历史学与计算机科学交叉学科,药学与其他学科的交叉学科有哪些?需要学习什么课..._药学职称考试_帮考网...
  12. 专属于程序猿的成语,进来看,进来笑!
  13. ADS系列之SIPI仿真笔记:ADS多种版本的安装、卸载重装(一)
  14. EPICS--areaDetector--ADEiger
  15. Self-supervised Graph Learning for Recommendation
  16. S32K144的FLASH中的SDK函数(FLASH_DRV_EraseSector)不能正常执行
  17. 【补题计划】Codeforces Round #533+#534(Div.2)
  18. kali 2020 换源、中文支持
  19. 交换机常用命令(1)
  20. 【JZOJ B组】【GDKOI2004】使命的召唤

热门文章

  1. linux开发板调试,用arm-gdb和gdbserver调试开发板的方法
  2. java自带的字节码技术_读懂字节码-还原JAVA源码
  3. stream 上传插件 java_stream: 流式(包含断点续传)上传文件,包括前端和java后台...
  4. mysql 字符串出现问题_MYSQL 中字符串函数 归纳总结
  5. 获取小程序 openid
  6. 对话Google全球VP Jay Yagnik:TensorFlow2.0会强化可控性
  7. 对话彭军、楼教主:1.12亿美元融资来了,Pony.ai车队也已在路上 | 变局者
  8. ubuntu-16.04安装程序报错 you might want to run 'apt-get -f install' to correct these
  9. golang 中io包用法(一)
  10. Setting property 'source' to 'org.eclipse.jst.jee.