• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • Android Path Time ScrollBar(Path 時間軸)

    版本號:1.0
    日期:2014.4.22
    版權: 2014 kince 轉載注明出處

      這是仿Path2.0UI的一個demo的截圖,我最早是在農民伯伯的這篇博客中看到的【Andorid X 項目筆記】開源項目使用(6),他說這個程序是反編譯Path的。可是這次我特地看了一下代碼,發現事實上不是這種。原帖地址應該是這個:http://www.eoeandroid.com/forum.php?mod=viewthread&tid=187725,作者使用的是github上的一個開源項目:Android-ScrollBarPanel,它實現的效果例如以下:


      已經非常接近Path的效果了。還有墨跡天氣的實景也是使用了這種效果:

      并且,墨跡天氣用的也是這個開源項目。效果什么基本都沒改。所以以下重點說一下這個開源項目的實現。

    在看它的代碼之前先來分析一下這個效果該怎樣實現,它就是在滾動欄(scrollbar)的旁邊動態顯示一個View。這個View里面顯示的內容會隨著滾動欄的位置變化而變化。一般像帶滑動效果的容器控制都會有滾動欄,比方ScrollView、ListView、GeidView等。那這個滾動欄究竟是什么呢?它是一個View的屬性,該屬性是繼承view的, 目的設置滾動欄顯示。有以下設置none(隱藏)。horizontal(水平),vertical (垂直)。并非全部的view設置就有效果。 LinearLayout 設置也沒有效果。 要想在超過一屏時拖動效果,在最外層加上ScrollView。并且能夠自己定義滾動欄的樣式和位置。但Path用的并非自己定義的滾動欄,它是在滾動欄旁邊加的View。如圖:


      若是在滾動欄的旁邊加入顯示View,首先須要獲取滾動欄的位置,當在滑動的時候在顯示滾動欄的同一時候也讓加入的View顯示出來,也就是說它和滾動欄的顯示是同步的。

    那究竟怎樣實現呢。帶著這些疑問看一下源代碼:

    package com.dafruits.android.library.widgets;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.os.Handler;
    import android.util.AttributeSet;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewConfiguration;
    import android.view.animation.Animation;
    import android.view.animation.Animation.AnimationListener;
    import android.view.animation.AnimationUtils;
    import android.widget.AbsListView;
    import android.widget.AbsListView.OnScrollListener;
    import android.widget.ListView;
    
    import com.dafruits.android.library.R;
    
    public class ExtendedListView extends ListView implements OnScrollListener {
    
         public static interface OnPositionChangedListener {
    
              public void onPositionChanged(ExtendedListView listView, int position, View scrollBarPanel);
    
         }
    
         private OnScrollListener mOnScrollListener = null;
    
         private View mScrollBarPanel = null;
         private int mScrollBarPanelPosition = 0;
    
         private OnPositionChangedListener mPositionChangedListener;
         private int mLastPosition = -1;
    
         private Animation mInAnimation = null;
         private Animation mOutAnimation = null;
    
         private final Handler mHandler = new Handler();
    
         private final Runnable mScrollBarPanelFadeRunnable = new Runnable() {
    
              @Override
              public void run() {
                   if (mOutAnimation != null) {
                        mScrollBarPanel.startAnimation(mOutAnimation);
                   }
              }
         };
    
         /*
         * keep track of Measure Spec
         */
         private int mWidthMeasureSpec;
         private int mHeightMeasureSpec;
    
         public ExtendedListView(Context context) {
              this(context, null);
         }
    
         public ExtendedListView(Context context, AttributeSet attrs) {
              this(context, attrs, android.R.attr.listViewStyle);
         }
    
         public ExtendedListView(Context context, AttributeSet attrs, int defStyle) {
              super(context, attrs, defStyle);
    
              super.setOnScrollListener(this);
    
              final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ExtendedListView);
              final int scrollBarPanelLayoutId = a.getResourceId(R.styleable.ExtendedListView_scrollBarPanel, -1);
              final int scrollBarPanelInAnimation = a.getResourceId(R.styleable.ExtendedListView_scrollBarPanelInAnimation, R.anim.in_animation);
              final int scrollBarPanelOutAnimation = a.getResourceId(R.styleable.ExtendedListView_scrollBarPanelOutAnimation, R.anim.out_animation);
              a.recycle();
    
              if (scrollBarPanelLayoutId != -1) {
                   setScrollBarPanel(scrollBarPanelLayoutId);
              }
    
              final int scrollBarPanelFadeDuration = ViewConfiguration.getScrollBarFadeDuration();
    
              if (scrollBarPanelInAnimation > 0) {
                   mInAnimation = AnimationUtils.loadAnimation(getContext(), scrollBarPanelInAnimation);
              }
             
              if (scrollBarPanelOutAnimation > 0) {
                   mOutAnimation = AnimationUtils.loadAnimation(getContext(), scrollBarPanelOutAnimation);
                   mOutAnimation.setDuration(scrollBarPanelFadeDuration);
    
                   mOutAnimation.setAnimationListener(new AnimationListener() {
    
                        @Override
                        public void onAnimationStart(Animation animation) {
                        }
    
                        @Override
                        public void onAnimationRepeat(Animation animation) {
    
                        }
    
                        @Override
                        public void onAnimationEnd(Animation animation) {
                             if (mScrollBarPanel != null) {
                                  mScrollBarPanel.setVisibility(View.GONE);
                             }
                        }
                   });
              }
         }
    
         @Override
         public void onScrollStateChanged(AbsListView view, int scrollState) {
              if (mOnScrollListener != null) {
                   mOnScrollListener.onScrollStateChanged(view, scrollState);
              }
         }
    
         @Override
         public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
              if (null != mPositionChangedListener && null != mScrollBarPanel) {
    
                   // Don't do anything if there is no itemviews
                   if (totalItemCount > 0) {
                        /*
                        * from android source code (ScrollBarDrawable.java)
                        */
                        final int thickness = getVerticalScrollbarWidth();
                        int height = Math.round((float) getMeasuredHeight() * computeVerticalScrollExtent() / computeVerticalScrollRange());
                        int thumbOffset = Math.round((float) (getMeasuredHeight() - height) * computeVerticalScrollOffset() / (computeVerticalScrollRange() - computeVerticalScrollExtent()));
                        final int minLength = thickness * 2;
                        if (height < minLength) {
                             height = minLength;
                        }
                        thumbOffset += height / 2;
                       
                        /*
                        * find out which itemviews the center of thumb is on
                        */
                        final int count = getChildCount();
                        for (int i = 0; i < count; ++i) {
                             final View childView = getChildAt(i);
                             if (childView != null) {
                                  if (thumbOffset > childView.getTop() && thumbOffset < childView.getBottom()) {
                                       /*
                                       * we have our candidate
                                       */
                                       if (mLastPosition != firstVisibleItem + i) {
                                            mLastPosition = firstVisibleItem + i;
                                           
                                            /*
                                            * inform the position of the panel has changed
                                            */
                                            mPositionChangedListener.onPositionChanged(this, mLastPosition, mScrollBarPanel);
                                           
                                            /*
                                            * measure panel right now since it has just changed
                                            *
                                            * INFO: quick hack to handle TextView has ScrollBarPanel (to wrap text in
                                            * case TextView's content has changed)
                                            */
                                            measureChild(mScrollBarPanel, mWidthMeasureSpec, mHeightMeasureSpec);
                                       }
                                       break;
                                  }
                             }
                        }
    
                        /*
                        * update panel position
                        */
                        mScrollBarPanelPosition = thumbOffset - mScrollBarPanel.getMeasuredHeight() / 2;
                        final int x = getMeasuredWidth() - mScrollBarPanel.getMeasuredWidth() - getVerticalScrollbarWidth();
                        mScrollBarPanel.layout(x, mScrollBarPanelPosition, x + mScrollBarPanel.getMeasuredWidth(),
                                  mScrollBarPanelPosition + mScrollBarPanel.getMeasuredHeight());
                   }
              }
    
              if (mOnScrollListener != null) {
                   mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
              }
         }
    
         public void setOnPositionChangedListener(OnPositionChangedListener onPositionChangedListener) {
              mPositionChangedListener = onPositionChangedListener;
         }
    
         @Override
         public void setOnScrollListener(OnScrollListener onScrollListener) {
              mOnScrollListener = onScrollListener;
         }
    
         public void setScrollBarPanel(View scrollBarPanel) {
              mScrollBarPanel = scrollBarPanel;
              mScrollBarPanel.setVisibility(View.GONE);
              requestLayout();
         }
    
         public void setScrollBarPanel(int resId) {
              setScrollBarPanel(LayoutInflater.from(getContext()).inflate(resId, this, false));
         }
    
         public View getScrollBarPanel() {
              return mScrollBarPanel;
         }
        
         @Override
         protected boolean awakenScrollBars(int startDelay, boolean invalidate) {
              final boolean isAnimationPlayed = super.awakenScrollBars(startDelay, invalidate);
             
              if (isAnimationPlayed == true && mScrollBarPanel != null) {
                   if (mScrollBarPanel.getVisibility() == View.GONE) {
                        mScrollBarPanel.setVisibility(View.VISIBLE);
                        if (mInAnimation != null) {
                             mScrollBarPanel.startAnimation(mInAnimation);
                        }
                   }
                  
                   mHandler.removeCallbacks(mScrollBarPanelFadeRunnable);
                   mHandler.postAtTime(mScrollBarPanelFadeRunnable, AnimationUtils.currentAnimationTimeMillis() + startDelay);
              }
    
              return isAnimationPlayed;
         }
    
         @Override
         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
              super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
              if (mScrollBarPanel != null && getAdapter() != null) {
                   mWidthMeasureSpec = widthMeasureSpec;
                   mHeightMeasureSpec = heightMeasureSpec;
                   measureChild(mScrollBarPanel, widthMeasureSpec, heightMeasureSpec);
              }
         }
    
         @Override
         protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
              super.onLayout(changed, left, top, right, bottom);
    
              if (mScrollBarPanel != null) {
                   final int x = getMeasuredWidth() - mScrollBarPanel.getMeasuredWidth() - getVerticalScrollbarWidth();
                   mScrollBarPanel.layout(x, mScrollBarPanelPosition, x + mScrollBarPanel.getMeasuredWidth(),
                             mScrollBarPanelPosition + mScrollBarPanel.getMeasuredHeight());
              }
         }
    
         @Override
         protected void dispatchDraw(Canvas canvas) {
              super.dispatchDraw(canvas);
    
              if (mScrollBarPanel != null && mScrollBarPanel.getVisibility() == View.VISIBLE) {
                   drawChild(canvas, mScrollBarPanel, getDrawingTime());
              }
         }
    
         @Override
         public void onDetachedFromWindow() {
              super.onDetachedFromWindow();
    
              mHandler.removeCallbacks(mScrollBarPanelFadeRunnable);
         }
    }
      通過閱讀源代碼發現,這是一個自己定義的ListView,并且繼承了OnScrollListener接口。這個接口是在AbsListView.java里面定義的。主要是負責滑動事件的處理,它的代碼例如以下:
     /**
         * Interface definition for a callback to be invoked when the list or grid
         * has been scrolled.
         */
        public interface OnScrollListener {
    
            /**
             * The view is not scrolling. Note navigating the list using the trackball counts as
             * being in the idle state since these transitions are not animated.
             */
            public static int SCROLL_STATE_IDLE = 0;
    
            /**
             * The user is scrolling using touch, and their finger is still on the screen
             */
            public static int SCROLL_STATE_TOUCH_SCROLL = 1;
    
            /**
             * The user had previously been scrolling using touch and had performed a fling. The
             * animation is now coasting to a stop
             */
            public static int SCROLL_STATE_FLING = 2;
    
            /**
             * Callback method to be invoked while the list view or grid view is being scrolled. If the
             * view is being scrolled, this method will be called before the next frame of the scroll is
             * rendered. In particular, it will be called before any calls to
             * {@link Adapter#getView(int, View, ViewGroup)}.
             *
             * @param view The view whose scroll state is being reported
             *
             * @param scrollState The current scroll state. One of {@link #SCROLL_STATE_IDLE},
             * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}.
             */
            public void onScrollStateChanged(AbsListView view, int scrollState);
    
            /**
             * Callback method to be invoked when the list or grid has been scrolled. This will be
             * called after the scroll has completed
             * @param view The view whose scroll state is being reported
             * @param firstVisibleItem the index of the first visible cell (ignore if
             *        visibleItemCount == 0)
             * @param visibleItemCount the number of visible cells
             * @param totalItemCount the number of items in the list adaptor
             */
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
                    int totalItemCount);
        }
      OnScrollListener定義了三個常量。分別表示當屏幕停止滾動時為0;當屏幕滾動且用戶使用的觸碰或手指還在屏幕上時為1;由于用戶的操作。屏幕產生慣性滑動時為2。詳細解釋例如以下:
    new OnScrollListener() {   
            boolean isLastRow = false;   
           
            @Override   
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {   
                //滾動時一直回調,直到停止滾動時才停止回調。單擊時回調一次。   
                //firstVisibleItem:當前能看見的第一個列表項ID(從0開始)   
                //visibleItemCount:當前能看見的列表項個數(小半個也算)   
                //totalItemCount:列表項共數   
           
                //推斷是否滾到最后一行   
                if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {   
                    isLastRow = true;   
                }   
            }   
            @Override   
            public void onScrollStateChanged(AbsListView view, int scrollState) {   
                //正在滾動時回調,回調2-3次,手指沒拋則回調2次。
    

    scrollState = 2的這次不回調 //回調順序例如以下 //第1次:scrollState = SCROLL_STATE_TOUCH_SCROLL(1) 正在滾動 //第2次:scrollState = SCROLL_STATE_FLING(2) 手指做了拋的動作(手指離開屏幕前,用力滑了一下) //第3次:scrollState = SCROLL_STATE_IDLE(0) 停止滾動 //當屏幕停止滾動時為0;當屏幕滾動且用戶使用的觸碰或手指還在屏幕上時為1。 //由于用戶的操作,屏幕產生慣性滑動時為2 //當滾到最后一行且停止滾動時,運行載入 if (isLastRow && scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) { //載入元素 ...... isLastRow = false; } } }

      了解完OnScrollListener這個接口再回頭看一下代碼,首先定義了一個回調:
       public static interface OnPositionChangedListener {
    
              public void onPositionChanged(ExtendedListView listView, int position,
                        View scrollBarPanel);
    
         }
      這個用來在Activity中設置監聽事件的,Activity的代碼例如以下:
    package com.dafruits.android.samples;
    
    import android.app.Activity;
    import android.graphics.Color;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.TextView;
    
    import com.dafruits.android.library.widgets.ExtendedListView;
    import com.dafruits.android.library.widgets.ExtendedListView.OnPositionChangedListener;
    
    public class DemoScrollBarPanelActivity extends Activity implements OnPositionChangedListener {
    
         private ExtendedListView mListView;
    
         @Override
         public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
    
              setContentView(R.layout.main);
    
              mListView = (ExtendedListView) findViewById(android.R.id.list);
              mListView.setAdapter(new DummyAdapter());
              mListView.setCacheColorHint(Color.TRANSPARENT);
              mListView.setOnPositionChangedListener(this);
         }
    
         private class DummyAdapter extends BaseAdapter {
    
              private int mNumDummies = 100;
    
              @Override
              public int getCount() {
                   return mNumDummies;
              }
    
              @Override
              public Object getItem(int position) {
                   return position;
              }
    
              @Override
              public long getItemId(int position) {
                   return position;
              }
    
              @Override
              public View getView(int position, View convertView, ViewGroup parent) {
                   if (convertView == null) {
                        convertView = LayoutInflater.from(DemoScrollBarPanelActivity.this).inflate(R.layout.list_item, parent,
                                  false);
                   }
    
                   TextView textView = (TextView) convertView;
                   textView.setText("" + position);
    
                   return convertView;
              }
         }
    
         @Override
         public void onPositionChanged(ExtendedListView listView, int firstVisiblePosition, View scrollBarPanel) {
              ((TextView) scrollBarPanel).setText("Position " + firstVisiblePosition);
         }
    }
      接著看一下第三個構造方法,由于這個自己定義的ListView定義了自己的屬性。所以須要從attrs文件里來取出這些屬性。自己定義的屬性包含三個部分,一是在ListView滑動時彈出的View,二是這個View彈出時的動畫,三是這個View消失時的動畫。然后開始設置這個彈出的View:
      if (scrollBarPanelLayoutId != -1) {
                   setScrollBarPanel(scrollBarPanelLayoutId);
              }
      看一下設置的方法。
      public void setScrollBarPanel(View scrollBarPanel) {
              mScrollBarPanel = scrollBarPanel;
              mScrollBarPanel.setVisibility(View.GONE);
              requestLayout();
         }
    
         public void setScrollBarPanel(int resId) {
              setScrollBarPanel(LayoutInflater.from(getContext()).inflate(resId,
                        this, false));
         }
      
      先是調用以下這種方法。從xml文件里載入彈出View的布局,在這個地方須要說一下假設自己定義的View不須要手動繪制的話,那么就能夠使用LayoutInflater去在xml中載入一個已經配置好的視圖,本例中就是使用這個方式。這樣mScrollBarPanel就儲存了彈出的View。然后設置為不可見。使用requestLayout()刷新一下視圖。再接著就是載入兩個彈出的動畫。特別的。在mOutAnimation動畫中設置了監聽器,在動畫結束的時候設置彈出的View不可見。

      回到第三個構造方法中,在第二行設置了super.setOnScrollListener(this),這種方法是效果實現的關鍵。為什么這么說。先看一下它的源代碼。

    它是在AbsListView中定義的。

       /**
         * Set the listener that will receive notifications every time the list scrolls.
         *
         * @param l the scroll listener
         */
        public void setOnScrollListener(OnScrollListener l) {
            mOnScrollListener = l;
            invokeOnItemScrollListener();
        }
      設置這種方法后,會傳遞一個OnScrollListener對象給mOnScrollListener,然后調用invokeOnItemScrollListener()方法,它的代碼例如以下:
     /**
         * Notify our scroll listener (if there is one) of a change in scroll state
         */
        void invokeOnItemScrollListener() {
            if (mFastScroller != null) {
                mFastScroller.onScroll(this, mFirstPosition, getChildCount(), mItemCount);
            }
            if (mOnScrollListener != null) {
                mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount);
            }
            onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
        }
      假設mOnScrollListener不為空的話,就調用mOnScrollListener的onScroll方法。而onScroll方法正是OnScrollListener接口定義的抽象方法,由于我們在ListView中繼承了OnScrollListener接口,重載了onScroll方法,所以將會調用我們自己實現的onScroll方法。就是這樣一個流程。
      然后看一下onScroll方法的實現,
    @Override
         public void onScroll(AbsListView view, int firstVisibleItem,
                   int visibleItemCount, int totalItemCount) {
              Log.i("onScroll", "onScroll");
              if (null != mPositionChangedListener && null != mScrollBarPanel) {
    
                   // Don't do anything if there is no itemviews
                   if (totalItemCount > 0) {
                        /*
                        * from android source code (ScrollBarDrawable.java)
                        */
                        final int thickness = getVerticalScrollbarWidth();
                        int height = Math.round((float) getMeasuredHeight()
                                  * computeVerticalScrollExtent()
                                  / computeVerticalScrollRange());
                        int thumbOffset = Math
                                  .round((float) (getMeasuredHeight() - height)
                                            * computeVerticalScrollOffset()
                                            / (computeVerticalScrollRange() - computeVerticalScrollExtent()));
                        final int minLength = thickness * 2;
                        if (height < minLength) {
                             height = minLength;
                        }
                        thumbOffset += height / 2;
    
                        /*
                        * find out which itemviews the center of thumb is on
                        */
                        final int count = getChildCount();
                        for (int i = 0; i < count; ++i) {
                             final View childView = getChildAt(i);
                             if (childView != null) {
                                  if (thumbOffset > childView.getTop()
                                            && thumbOffset < childView.getBottom()) {
                                       /*
                                       * we have our candidate
                                       */
                                       if (mLastPosition != firstVisibleItem + i) {
                                            mLastPosition = firstVisibleItem + i;
    
                                            /*
                                            * inform the position of the panel has changed
                                            */
                                            mPositionChangedListener.onPositionChanged(
                                                      this, mLastPosition, mScrollBarPanel);
    
                                            /*
                                            * measure panel right now since it has just
                                            * changed
                                            * 
                                            * INFO: quick hack to handle TextView has
                                            * ScrollBarPanel (to wrap text in case
                                            * TextView's content has changed)
                                            */
                                            measureChild(mScrollBarPanel,
                                                      mWidthMeasureSpec, mHeightMeasureSpec);
                                       }
                                       break;
                                  }
                             }
                        }
    
                        /*
                        * update panel position
                        */
                        mScrollBarPanelPosition = thumbOffset
                                  - mScrollBarPanel.getMeasuredHeight() / 2;
                        final int x = getMeasuredWidth()
                                  - mScrollBarPanel.getMeasuredWidth()
                                  - getVerticalScrollbarWidth();
                        mScrollBarPanel.layout(
                                  x,
                                  mScrollBarPanelPosition,
                                  x + mScrollBarPanel.getMeasuredWidth(),
                                  mScrollBarPanelPosition
                                            + mScrollBarPanel.getMeasuredHeight());
                   }
              }
    
              if (mOnScrollListener != null) {
                   mOnScrollListener.onScroll(view, firstVisibleItem,
                             visibleItemCount, totalItemCount);
              }
         }
      上面已經說到。這個onScroll是隨著滑動而一直調用的,而我們的需求就是在滑動的時候彈出一個View來。所以這種方法正是處理問題的關鍵位置。能夠在這里繪制彈出View的視圖。從上面的代碼也能夠看出,就是在這里進行彈出View大小的計算以及位置的設定等。
       最后就是之前說的自己定義ViewGroup的問題了。重載onMeasure()、onLayout()、ondispatchDraw()方法了,這個在本例中也是有所體現的,只是都比較簡單,相信都看得懂。

    可是這幾個方法都是在View初始化的時候調用的,并且僅僅是調用一次。這樣并不適合動態的繪制視圖。所以這也是為什么本樣例繼承了OnScrollListener,然后在其onScroll方法中去繪制視圖。由于onScroll方法在滑動的時候會調用,所以在滑動的時候就會繪制視圖了。

    因此也能夠看出本例採用的是動態畫圖的方式,不是顯示隱藏的方式。

       











    本文轉自mfrbuaa博客園博客,原文鏈接:http://www.cnblogs.com/mfrbuaa/p/5396685.html,如需轉載請自行聯系原作者
    版權聲明:本文為weixin_33862993原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/weixin_33862993/article/details/90156204

    智能推薦

    Android 自定義時間軸

    自定義時間軸 可配合 recyclerView 進行使用 動態的指點具體的位置 高度自定義,根據需求修改 View 即可 可設置默認 圓點 的選中,以及修改其位置 效果圖如下: 布局如下: 使用: 1,需要配合 recyclerView 使用。也可單獨使用 2,調用 setSelect 方法設置是否選中默認的,以及默認圓的位置,可通過百分比,或者 具體的Y軸值來設置。 3,通過調用 add 方法添...

    android時間軸(TimeLine)(recyclerView實現)

    個人需求 最近項目有個時間軸的需求,如下圖。(數據不同,背景不同) 需求實現 先扒一扒demo中用到的工具類。 然后就是對數據的bean類保存了 (實際開發最好私有化變量 提供get set方法,這里demo簡單化) 最后就是主要的代碼編寫了 條目的xml布局還是貼一下吧 背景 文中的共有四個背景,其實就是四個shape。 代碼已經放在這里了,歡迎查看,我寫的不好的或者不清楚的也請指出。 http...

    android繪圖之Path總結

          我們在做自定義View的時候,很多時候都會用到Path,即路徑,今天總結一下Path有哪些,以及他們的常用方法。 Path常用方法: 方法 作用 備注 moveTo 移動起點 移動下一次操作的起點位置 lineTo 連接直線 連接上一個點到當前點之間的直線 setLastPoint 設置終點 重置最后一個點的位置 close ...

    Android Paint 之 獲取繪制的 Path 文本的Path

    部分轉載于: http://hencoder.com/ui-1-2/ 獲取實際的Path 首先解答第一個問題:「實際 Path」。所謂實際 Path ,指的就是 drawPath() 的繪制內容的輪廓,要算上線條寬度和設置的 PathEffect。 默認情況下(線條寬度為 0、沒有 PathEffect),原 Path 和實際 Path 是一樣的;而在線條寬度不為 0 (并且模式為 STROKE ...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    猜你喜歡

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    精品国产乱码久久久久久蜜桃不卡