Dynamic clock

本文介绍了一种在Android应用中实现动态时钟图标的方案,通过自定义Drawable和定时更新图标来达到显示实时时间的效果,并解决了由此引发的内存泄漏问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载自:http://blog.youkuaiyun.com/pinsengjixuhezhou/article/details/45890569

注:这篇文章 主要内容和思路还是转自上面链接中博主的文章,我在实际使用过程中根据自己的需求有些调整。


主要要的逻辑就是想办法替换掉应用icon的drawable,然后通过自定义drawable的方式来实现动态Icon。

直接贴代码吧,没什么难的。

==============================================================================

2016.5.20号更新:

       因为这个时钟图标导致内存泄漏的问题害我查了一个多星期的代码,之前没注意是因为这个时钟图标引起的问题,用mat查看发现这个时钟图标只占了那么几k的内存,以为没什么影响,结果是因为我在时钟图标里用的是3张bitmap不停叠加生成的实时图标,当热插拔sim卡时,会destroy掉Luancher,而我这边没有做bitmap的recycle操作,导致内存里的bitmap一直不停增加。

          在这里重新添加资源释放的修改。

==============================================================================

首先在IconCache.java 中的修改


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private static class CacheEntry {  
  2.         public Bitmap icon;  
  3.         public CharSequence title;  
  4.         public CharSequence contentDescription;  
  5.         public IconScript script;//icon的脚本  
  6.     }  

    public IconScript getScript(Intent intent, UserHandleCompat user){  
        synchronized (mCache) {  
             ComponentName component = intent.getComponent();  
             if (component == null) {  
                 return null;  
             }  
            LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user);  
            CacheEntry entry = cacheLocked(component, launcherActInfo, user, true, false);  
            if("com.android.deskclock".equals(component.getPackageName())) {
                if (ClockScript.getClockInstance() == null){
                	entry.script = new ClockScript(); 
                } else {
                	entry.script = ClockScript.getClockInstance();
                }
            	
            } 
//            if("com.android.calendar".equals(componentName.getPackageName())) {
//                entry.script = new CalendarScript();  
//            } 
            return entry.script;  
        }  
    } 



ShortcutInfo.java

[java]  view plain  copy
  1. public IconScript getScript(IconCache iconCache){  
  2.     return iconCache.getScript(promisedIntent != null ? promisedIntent : intent, user);  
  3. }  

Launcher.java , 新增一个Callbacks接口,在onDestroy()的调用,以释放时钟图标的资源

    @Thunk WeakReference<ClockCallbacks> mClockCallbacks;
    public interface ClockCallbacks {
    	public void stopClockIcon();
    }
    
    @Thunk final Object mLock = new Object();
    public void initClockCallbak(ClockCallbacks callbacks){
        synchronized (mLock) {
            mClockCallbacks = new WeakReference<ClockCallbacks>(callbacks);
        }
    }
    @Override
    public void onDestroy() {
    ……………………
        if (mClockCallbacks != null){
            ClockCallbacks callbacks = mClockCallbacks.get();
            if (callbacks != null) {
                callbacks.stopClockIcon();
            }
        }
    }




BubbleTextView.java 继承Launcher.ClockCallBacks接口

public class BubbleTextView extends TextView
        implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView , Launcher.ClockCallbacks{  //tangxuankai add Launcher.ClockCallbacks
   ………………
}


[java]  view plain  copy
  1. public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,  
  2.             boolean setDefaultPadding, boolean promiseStateChanged) {  
  3. ……  
  4.  mScript = info.getScript(iconCache);  
  5. ……  
  6. }  
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    private Drawable setIcon(Drawable icon, int iconSize) {
        mIcon = icon;
        if (iconSize != -1) {
            mIcon.setBounds(0, 0, iconSize, iconSize);
        }
        if (mLayoutHorizontal) {
            if (Utilities.ATLEAST_JB_MR1) {
                setCompoundDrawablesRelative(mIcon, null, null, null);
            } else {
                setCompoundDrawables(mIcon, null, null, null);
            }
        } else {
        	/****************tangxuankai add start ***********************/
	        if(mScript != null){
	        	mLauncher.initClockCallbak(this);
	        	mScript.setBounds(0, 0, iconSize, iconSize);  
	        	mIcon = mScript;
	            if(!mScript.isRuning){
	            	mScript.run(this);  
	            }
	        }
	        /****************tangxuankai add end ***********************/
            setCompoundDrawables(null, mIcon, null, null);
        }
        return icon;
    }

ClockScript.java

/** 
 * dynamic Clock Icon
 * 
 */  
public class ClockScript extends IconScript {
	Rect    mSrcRect  = new Rect();  
	Rect    mDestRect = new Rect();  
	private int     mIconSize;
	private Context mContext; 
	private Time    mTime;
	private Bitmap  mSecond, mMinute, mHour, mBackground; 
	private int     mHiegtOffset, mWidthOffset;
	public static  ClockScript mClockInstance;
    /** 
     * Results show target View 
     */  
    private View mView;  
    /** 
     * notify System to invalidate 
     */  
    private ClockThread mClockThread = null;  
    /** 
     * is showen on the screen now 
     */  
    private boolean mIsShowInScreen = false;  
      
    public static ClockScript getClockInstance(){
    	return mClockInstance;
    }
    public ClockScript(){  
        super();  
        mClockInstance = this;
        mContext  = LauncherAppState.getInstance().getContext();
        mIconSize = LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize;
    }  
    public void run(View view) {  
        mView       = view;  
        mTime       = new Time();
        mBackground = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.launcher_theme_ic_app_clock_bg);//getBounds();  
        mSecond     = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.launcher_theme_ic_app_clock_second_draw_by_pic);
        mMinute     = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.launcher_theme_ic_app_clock_minute_draw_by_pic);
        mHour       = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.launcher_theme_ic_app_clock_hour_draw_by_pic);
        mDestRect.set(0, 0, mIconSize, mIconSize);
        mSrcRect.set(0, 0, mBackground.getWidth(), mBackground.getHeight());
        mHiegtOffset = (mIconSize - mBackground.getHeight()) / 2;
        mWidthOffset = (mIconSize - mSecond.getWidth()) / 2;
        if(mClockThread == null){  
            mClockThread = new ClockThread();  
            mClockThread.start();  
        }  
    }  
      
    @Override  
    public void onPause() {  
        mClockThread.pauseRun();  
        super.onPause();  
    }  
      
    @Override  
    public void onResume() {  
        mClockThread.resumeRun();  
        super.onResume();  
    }  
      
    @Override  
    public void onStop() {  
    	if (mClockThread != null){
    		mClockThread.stopRun();
            mClockThread.interrupt();
            mClockThread = null;	
    	}
        
        super.onStop();  
        mContext = null;
        if (mBackground != null){
        	mBackground.recycle();
        	mBackground = null;
        }
        if (mSecond != null){
        	mSecond.recycle();
        	mSecond = null;
        }
        if (mMinute != null){
        	mMinute.recycle();
        	mMinute = null;
        }
        if (mHour != null){
        	mHour.recycle();
        	mHour = null;
        }
        mClockInstance = null;
    }  
      
  
    @Override  
    public void draw(Canvas canvas) {  
        super.draw(canvas);  
        mIsShowInScreen = true;  
        drawIndicator(canvas);  
        if(mClockThread.wait){  
            mClockThread.resumeRun();  
        }  
    }  
    /** 
     * draw Indicator 
     * @param canvas 
     * @param centerX 
     * @param centerY 
     * @param p 
     */  
    private void drawIndicator(Canvas canvas){  
        try{
        	 mTime.setToNow();  
//           p.setStrokeWidth(2);  
//           p.setColor(Color.BLACK);  

           float hourAngle = (float)mTime.hour  / 12 * 360 + (float)mTime.minute / 60 * (360 / 12);
           float minAngle  = (float)mTime.minute / 60 * 360;  
           float secAngle  = (float)mTime.second / 60 * 360; 
           
           
           
           canvas.save();
           canvas.drawBitmap(mBackground, mSrcRect, mDestRect, null);
           
           canvas.restore(); 
           canvas.save();
           canvas.rotate(hourAngle, mIconSize / 2, mIconSize / 2);
           canvas.drawBitmap(mHour, mWidthOffset, mHiegtOffset, null);
           
           canvas.restore(); 
           canvas.save();
           canvas.rotate(minAngle, mIconSize / 2, mIconSize / 2);
           canvas.drawBitmap(mMinute, mWidthOffset, mHiegtOffset, null);
           
           canvas.restore(); 
           canvas.save();
           canvas.rotate(secAngle, mIconSize / 2, mIconSize / 2);
           canvas.drawBitmap(mSecond, mWidthOffset, mHiegtOffset, null);
           
           canvas.restore(); 
        }catch(Exception e){
        	e.printStackTrace();
        }
       
    }  
      
    class ClockThread extends Thread {  
        int times = 0;  
        boolean running = true;  
  
        public boolean wait = false;  
  
        public void stopRun() {  
            running = false;  
            synchronized (this) {  
                this.notify();  
            }  
        };  
  
        public void pauseRun() {  
            this.wait = true;  
            synchronized (this) {  
                this.notify();  
            }  
        }  
  
        public void resumeRun() {  
            this.wait = false;  
            synchronized (this) {  
                this.notify();  
            }  
        }  
  
        public void run() {  
            while (running) {  
                synchronized (mView) {  
                    mView.postInvalidate();  
                }  
                  
                if(!mIsShowInScreen){  
                    pauseRun();  
                }  
                mIsShowInScreen = false;  
                try {  
                    Thread.sleep(500);  
                } catch (Exception e) {  
                    System.out.println(e);  
                }  
                  
                synchronized (this) {  
                    if (wait) {  
                        try {  
                            wait();  
                        } catch (InterruptedException e) {  
                            // TODO Auto-generated catch block  
                            e.printStackTrace();  
                        }  
                    }  
                }  
            }  
        }  
    }  
  
}


分析一下这段代码如何按字节对齐的:`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: Gpixel CN // Engineer: // // Create Date: 2020/11/12 09:26:04 // Design Name: // Module Name: Serdes // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module Serdes( input ClkRx_Bit, input Clk_Stream, input Rst_Stream, input DataRx_p, input DataRx_n, output reg Stream_Valid, output reg [11 :0] Stream_Data, input [1 :0] BitMode, input BitToggle, input BitSlip, input Cmd_Align, output Ack_Align, input IODelayTapMan, input [4 :0] IODelayTapDataMan, output [4 :0] IODelayTapDataOut, output [4 :0] edge_s ); wire DataRx_IBUFDS; wire DataRx_IDELAYE2; wire [1 :0] DataRx_IDDR; reg [1 :0] DataRx_IDDR_d; reg [1 :0] DataRx; reg BitSlip_d = 1'b0; reg BitSlip_d2 = 1'b0; wire BitSlip_RE; reg src_BitSlip_RE = 1'b0; reg BitSlip_RE_q = 1'b0; reg BitSlip_Ack = 1'b0; reg BitSlip_Ack_q = 1'b0; reg BitToggle_d = 1'b0; reg BitToggle_d2 = 1'b0; wire BitToggleFlag; reg src_BitToggleFlag = 1'b0; reg [1 :0] BitMode_q = 2'b10; wire [4 :0] IODelayTapDataIn; wire [4 :0] IODelayTapDataAuto; reg StreamFifo_Rd = 1'b0; wire StreamFifo_Valid; wire StreamFifo_Empty; wire [11 :0] StreamFifo_Data; reg [2 :0] ShiftCnt = 3'd0; reg [11 :0] ShiftDataRx; reg [11 :0] DataRxPar_i = 12'd0; reg [11 :0] DataRxPar_i_d = 12'd0; //(*ASYNC_REG = "TRUE"*) reg [11 :0] DataRxBitEnable; reg [11 :0] Stream_Data_Align; reg [11 :0] Stream_Data_i; reg Stream_Valid_i; reg [2 :0] LoadParallelCnt = 3'd6; reg LoadParallel = 1'b0; reg LoadParallel_d = 1'b0; assign IODelayTapDataIn = (IODelayTapMan == 1'b1)? IODelayTapDataMan : IODelayTapDataAuto; //***********************************************************************************************// IBUFDS #( .DIFF_TERM("TRUE"), // Differential Termination .IBUF_LOW_PWR("FALSE"), // Low power="TRUE", Highest performance="FALSE" .IOSTANDARD("LVDS_25") // Specify the input I/O standard ) IBUFDS_inst ( .O(DataRx_IBUFDS), // Buffer output .I(DataRx_p), // Diff_p buffer input (connect directly to top-level port) .IB(DataRx_n) // Diff_n buffer input (connect directly to top-level port) ); // (* IODELAY_GROUP = <iodelay_group_TL> *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL IDELAYE2 #( .CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE) .DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN) .HIGH_PERFORMANCE_MODE("TRUE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE") .IDELAY_TYPE("VAR_LOAD"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE .IDELAY_VALUE(0), // Input delay tap setting (0-31) .PIPE_SEL("FALSE"), // Select pipelined mode, FALSE, TRUE .REFCLK_FREQUENCY(400.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0). .SIGNAL_PATTERN("DATA") // DATA, CLOCK input signal ) IDELAYE2_inst ( .CNTVALUEOUT(IODelayTapDataOut), // 5-bit output: Counter value output .DATAOUT(DataRx_IDELAYE2), // 1-bit output: Delayed data output .C(Clk_Stream), // 1-bit input: Clock input .CE(1'b0), // 1-bit input: Active high enable increment/decrement input .CINVCTRL(1'b0), // 1-bit input: Dynamic clock inversion input .CNTVALUEIN(IODelayTapDataIn), // 5-bit input: Counter value input .DATAIN(), // 1-bit input: Internal delay data input .IDATAIN(DataRx_IBUFDS), // 1-bit input: Data input from the I/O .INC(1'b0), // 1-bit input: Increment / Decrement tap delay input .LD(1'b1), // 1-bit input: Load IDELAY_VALUE input .LDPIPEEN(1'b0), // 1-bit input: Enable PIPELINE register to load data input .REGRST(1'b0) // 1-bit input: Active-high reset tap-delay input ); IDDR #( .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "OPPOSITE_EDGE", "SAME_EDGE" or "SAME_EDGE_PIPELINED" --<Q1><Q1> "SAME_EDGE_PIPELINED" --<Q1><Q1> "SAME_EDGE" .INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1 --<Q2><Q2> --<Q2><Q2> .INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1 .SRTYPE("ASYNC") // Set/Reset type: "SYNC" or "ASYNC" ) IDDR_inst ( .Q1(DataRx_IDDR[0]), // 1-bit output for positive edge of clock .Q2(DataRx_IDDR[1]), // 1-bit output for negative edge of clock .C(ClkRx_Bit), // 1-bit clock input .CE(1'b1), // 1-bit clock enable input .D(DataRx_IDELAYE2), // 1-bit DDR data input .R(Rst_Stream), // 1-bit reset .S(1'b0) // 1-bit set ); //***********************************************************************************************// always@(posedge ClkRx_Bit) begin BitMode_q <= BitMode; case(BitMode_q) 2'b00: begin LoadParallelCnt = 3'd4; DataRxBitEnable = 12'b111111110000; end 2'b01: begin LoadParallelCnt = 3'd5; DataRxBitEnable = 12'b111111111100; end 2'b10: begin LoadParallelCnt = 3'd6; DataRxBitEnable = 12'b111111111111; end default: begin LoadParallelCnt = 3'd6; DataRxBitEnable = 12'b111111111111; end endcase end //--------------------------------------------------------------------------// always@(posedge ClkRx_Bit) begin DataRx_IDDR_d <= DataRx_IDDR; if (BitToggleFlag == 1'b0) begin DataRx <= {DataRx_IDDR[0],DataRx_IDDR[1]}; end else begin DataRx <= {DataRx_IDDR_d[1],DataRx_IDDR[0]}; end end //--------------------------------------------------------------------------// always@(posedge ClkRx_Bit) begin ShiftDataRx <= {DataRx[0],DataRx[1],ShiftDataRx[11:2]}; LoadParallel_d <= LoadParallel; DataRxPar_i_d <= DataRxPar_i; if (BitSlip_RE == 1'b1) begin ShiftCnt <= ShiftCnt; LoadParallel <= 1'b0; end else begin if (ShiftCnt == LoadParallelCnt - 1'b1) begin ShiftCnt <= 3'd0; LoadParallel <= 1'b1; DataRxPar_i <= ShiftDataRx & DataRxBitEnable; end else begin ShiftCnt <= ShiftCnt + 3'd1; LoadParallel <= 1'b0; end end end //--------------------------------------------------------------------------// fifo_serdes_w16_d16 inst_fifo_serdes_w16_d16( .rst (Rst_Stream ), .wr_clk (ClkRx_Bit ), .rd_clk (Clk_Stream ), .din (DataRxPar_i_d ), .wr_en (LoadParallel_d ), .rd_en (StreamFifo_Rd ), .dout (StreamFifo_Data ), .full ( ), .empty (StreamFifo_Empty ), .valid (StreamFifo_Valid ) ); //--------------------------------------------------------------------------// always@(posedge Clk_Stream) begin if(StreamFifo_Empty == 1'b0) begin StreamFifo_Rd <= 1'b1; end else begin StreamFifo_Rd <= 1'b0; end case(BitMode) 2'b00: begin Stream_Data_i <= {4'b0000,StreamFifo_Data[11:4]}; end 2'b01: begin Stream_Data_i <= {2'b00,StreamFifo_Data[11:2]}; end 2'b10: begin Stream_Data_i <= StreamFifo_Data; end default: begin Stream_Data_i <= StreamFifo_Data; end endcase Stream_Valid_i <= StreamFifo_Valid; Stream_Valid <= Stream_Valid_i; Stream_Data <= Stream_Data_i; end //--------------------------------------------------------------------------// always@(posedge Clk_Stream) begin BitToggle_d <= BitToggle; BitToggle_d2 <= BitToggle_d; if ((BitToggle_d == 1'b1)&&(BitToggle_d2 == 1'b0)) begin src_BitToggleFlag <= ~src_BitToggleFlag; end BitSlip_d <= BitSlip; BitSlip_d2 <= BitSlip_d; if ((BitSlip_d == 1'b1)&&( BitSlip_d2 == 1'b0)) begin src_BitSlip_RE <= 1'b1; end else begin src_BitSlip_RE <= 1'b0; end end xpm_cdc_pulse #( .DEST_SYNC_FF(4), // DECIMAL; range: 2-10 .INIT_SYNC_FF(0), // DECIMAL; 0=disable simulation init values, 1=enable simulation init values .REG_OUTPUT(1), // DECIMAL; 0=disable registered output, 1=enable registered output .RST_USED(0), // DECIMAL; 0=no reset, 1=implement reset .SIM_ASSERT_CHK(0) // DECIMAL; 0=disable simulation messages, 1=enable simulation messages ) xpm_cdc_pulse_inst ( .dest_pulse(BitSlip_RE), // 1-bit output: Outputs a pulse the size of one dest_clk period when a pulse // transfer is correctly initiated on src_pulse input. This output is // combinatorial unless REG_OUTPUT is set to 1. .dest_clk(ClkRx_Bit), // 1-bit input: Destination clock. .dest_rst(), // 1-bit input: optional; required when RST_USED = 1 .src_clk(Clk_Stream), // 1-bit input: Source clock. .src_pulse(src_BitSlip_RE), // 1-bit input: Rising edge of this signal initiates a pulse transfer to the // destination clock domain. The minimum gap between each pulse transfer must be // at the minimum 2*(larger(src_clk period, dest_clk period)). This is measured // between the falling edge of a src_pulse to the rising edge of the next // src_pulse. This minimum gap will guarantee that each rising edge of src_pulse // will generate a pulse the size of one dest_clk period in the destination // clock domain. When RST_USED = 1, pulse transfers will not be guaranteed while // src_rst and/or dest_rst are asserted. .src_rst() // 1-bit input: optional; required when RST_USED = 1 ); //--------------------------------------------------------------------------// xpm_cdc_single #( .DEST_SYNC_FF(4), // DECIMAL; range: 2-10 .INIT_SYNC_FF(0), // DECIMAL; 0=disable simulation init values, 1=enable simulation init values .SIM_ASSERT_CHK(0), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages .SRC_INPUT_REG(1) // DECIMAL; 0=do not register input, 1=register input ) xpm_cdc_single_inst ( .dest_out(BitToggleFlag), // 1-bit output: src_in synchronized to the destination clock domain. This output is // registered. .dest_clk(ClkRx_Bit), // 1-bit input: Clock signal for the destination clock domain. .src_clk(Clk_Stream), // 1-bit input: optional; required when SRC_INPUT_REG = 1 .src_in(src_BitToggleFlag) // 1-bit input: Input signal to be synchronized to dest_clk domain. ); //--------------------------------------------------------------------------// BitAlign inst_BitAlign( .Clk (Clk_Stream), .Reset (Rst_Stream), .data_par (Stream_Data_i), .Cmd_Bit_Align (Cmd_Align), .Ack_Bit_Align (Ack_Align), .edge_s (edge_s), .loc_eye_sam (), .idelay_valuein (IODelayTapDataAuto) ); endmodule
最新发布
08-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值