欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android開發(fā)之自定義CheckBox

 更新時間:2016年08月11日 11:39:33   投稿:daisy  
本文通過實例代碼演示如何在Android中如何自定義CheckBox,實現(xiàn)的效果很好,有需要的可以參考借鑒。

要實現(xiàn)的效果如下

考慮到關(guān)鍵是動畫效果,所以直接繼承View。不過CheckBox的超類CompoundButton實現(xiàn)了Checkable接口,這一點值得借鑒。

下面記錄一下遇到的問題,并從源碼的角度解決。

問題一: 支持 wrap_content

由于是直接繼承自View,wrap_content需要進行特殊處理。

View measure流程的MeasureSpec:

 /**
  * A MeasureSpec encapsulates the layout requirements passed from parent to child.
  * Each MeasureSpec represents a requirement for either the width or the height.
  * A MeasureSpec is comprised of a size and a mode. 
  * MeasureSpecs are implemented as ints to reduce object allocation. This class
  * is provided to pack and unpack the <size, mode> tuple into the int.
  */
 public static class MeasureSpec {
  private static final int MODE_SHIFT = 30;
  private static final int MODE_MASK = 0x3 << MODE_SHIFT;

  /**
   * Measure specification mode: The parent has not imposed any constraint
   * on the child. It can be whatever size it wants.
   */
  public static final int UNSPECIFIED = 0 << MODE_SHIFT;

  /**
   * Measure specification mode: The parent has determined an exact size
   * for the child. The child is going to be given those bounds regardless
   * of how big it wants to be.
   */
  public static final int EXACTLY  = 1 << MODE_SHIFT;

  /**
   * Measure specification mode: The child can be as large as it wants up
   * to the specified size.
   */
  public static final int AT_MOST  = 2 << MODE_SHIFT;

  /**
   * Extracts the mode from the supplied measure specification.
   *
   * @param measureSpec the measure specification to extract the mode from
   * @return {@link android.view.View.MeasureSpec#UNSPECIFIED},
   *   {@link android.view.View.MeasureSpec#AT_MOST} or
   *   {@link android.view.View.MeasureSpec#EXACTLY}
   */
  public static int getMode(int measureSpec) {
   return (measureSpec & MODE_MASK);
  }

  /**
   * Extracts the size from the supplied measure specification.
   *
   * @param measureSpec the measure specification to extract the size from
   * @return the size in pixels defined in the supplied measure specification
   */
  public static int getSize(int measureSpec) {
   return (measureSpec & ~MODE_MASK);
  }
 }

從文檔說明知道android為了節(jié)約內(nèi)存,設(shè)計了MeasureSpec,它由modesize兩部分構(gòu)成,做這么多終究是為了從父容器向子view傳達長寬的要求。

mode有三種模式:

      1、UNSPECIFIED:父容器不對子view的寬高有任何限制

      2、EXACTLY:父容器已經(jīng)為子view指定了確切的寬高

      3、AT_MOST:父容器指定最大的寬高,子view不能超過

wrap_content屬于AT_MOST模式。

來看一下大致的measure過程:

在View中首先調(diào)用measure(),最終調(diào)用onMeasure()

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
 }

setMeasuredDimension設(shè)置view的寬高。再來看看getDefaultSize()

public static int getDefaultSize(int size, int measureSpec) {
  int result = size;
  int specMode = MeasureSpec.getMode(measureSpec);
  int specSize = MeasureSpec.getSize(measureSpec);

  switch (specMode) {
  case MeasureSpec.UNSPECIFIED:
   result = size;
   break;
  case MeasureSpec.AT_MOST:
  case MeasureSpec.EXACTLY:
   result = specSize;
   break;
  }
  return result;
 }

由于wrap_content屬于模式AT_MOST,所以寬高為specSize,也就是父容器的size,這就和match_parent一樣了。支持wrap_content總的思路是重寫onMeasure()具體點來說,模仿getDefaultSize()重新獲取寬高。

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  int heightSize = MeasureSpec.getSize(heightMeasureSpec);

  int width = widthSize, height = heightSize;

  if (widthMode == MeasureSpec.AT_MOST) {
   width = dp2px(DEFAULT_SIZE);
  }

  if (heightMode == MeasureSpec.AT_MOST) {
   height = dp2px(DEFAULT_SIZE);
  }
  setMeasuredDimension(width, height);
 }

問題二:Path.addPath()和PathMeasure結(jié)合使用

舉例子說明問題:

 mTickPath.addPath(entryPath);
 mTickPath.addPath(leftPath);
 mTickPath.addPath(rightPath);
 mTickMeasure = new PathMeasure(mTickPath, false);
 // mTickMeasure is a PathMeasure

盡管mTickPath現(xiàn)在是由三個path構(gòu)成,但是mTickMeasure此時的lengthentryPath長度是一樣的,到這里我就很奇怪了。看一下getLength()的源碼:

 /**
  * Return the total length of the current contour, or 0 if no path is
  * associated with this measure object.
  */
 public float getLength() {
  return native_getLength(native_instance);
 }

從注釋來看,獲取的是當前contour的總長。

getLength調(diào)用了native層的方法,到這里不得不看底層的實現(xiàn)了。

通過閱讀源代碼發(fā)現(xiàn),PathPathMeasure實際分別對應(yīng)底層的SKPathSKPathMeasure

查看native層的getLength()源碼:

 SkScalar SkPathMeasure::getLength() {
  if (fPath == NULL) {
   return 0;
  }
  if (fLength < 0) {
   this->buildSegments();
  }
  SkASSERT(fLength >= 0);
  return fLength;
}

實際上調(diào)用的buildSegments()來對fLength賦值,這里底層的設(shè)計有一個很聰明的地方——在初始化SKPathMeasure時對fLength做了特殊處理:

SkPathMeasure::SkPathMeasure(const SkPath& path, bool forceClosed) {
 fPath = &path;
 fLength = -1; // signal we need to compute it
 fForceClosed = forceClosed;
 fFirstPtIndex = -1;

 fIter.setPath(path, forceClosed);
}

fLength=-1時我們需要計算,也就是說當還沒有執(zhí)行過getLength()方法時,fLength一直是-1,一旦執(zhí)行則fLength>=0,則下一次就不會執(zhí)行buildSegments(),這樣避免了重復(fù)計算.

截取buildSegments()部分代碼:

void SkPathMeasure::buildSegments() {
 SkPoint   pts[4];
 int    ptIndex = fFirstPtIndex;
 SkScalar  distance = 0;
 bool   isClosed = fForceClosed;
 bool   firstMoveTo = ptIndex < 0;
 Segment*  seg;

 /* Note:
 * as we accumulate distance, we have to check that the result of +=
 * actually made it larger, since a very small delta might be > 0, but
 * still have no effect on distance (if distance >>> delta).
 *
 * We do this check below, and in compute_quad_segs and compute_cubic_segs
 */
 fSegments.reset();
 bool done = false;
 do {
  switch (fIter.next(pts)) {
   case SkPath::kMove_Verb:
    ptIndex += 1;
    fPts.append(1, pts);
    if (!firstMoveTo) {
     done = true;
     break;
    }
    firstMoveTo = false;
    break;

   case SkPath::kLine_Verb: {
    SkScalar d = SkPoint::Distance(pts[0], pts[1]);
    SkASSERT(d >= 0);
    SkScalar prevD = distance;
    distance += d;
    if (distance > prevD) {
     seg = fSegments.append();
     seg->fDistance = distance;
     seg->fPtIndex = ptIndex;
     seg->fType = kLine_SegType;
     seg->fTValue = kMaxTValue;
     fPts.append(1, pts + 1);
     ptIndex++;
    }
   } break;

   case SkPath::kQuad_Verb: {
    SkScalar prevD = distance;
    distance = this->compute_quad_segs(pts, distance, 0, kMaxTValue, ptIndex);
    if (distance > prevD) {
     fPts.append(2, pts + 1);
     ptIndex += 2;
    }
   } break;

   case SkPath::kConic_Verb: {
    const SkConic conic(pts, fIter.conicWeight());
    SkScalar prevD = distance;
    distance = this->compute_conic_segs(conic, distance, 0, kMaxTValue, ptIndex);
    if (distance > prevD) {
     // we store the conic weight in our next point, followed by the last 2 pts
     // thus to reconstitue a conic, you'd need to say
     // SkConic(pts[0], pts[2], pts[3], weight = pts[1].fX)
     fPts.append()->set(conic.fW, 0);
     fPts.append(2, pts + 1);
     ptIndex += 3;
    }
   } break;

   case SkPath::kCubic_Verb: {
    SkScalar prevD = distance;
    distance = this->compute_cubic_segs(pts, distance, 0, kMaxTValue, ptIndex);
    if (distance > prevD) {
     fPts.append(3, pts + 1);
     ptIndex += 3;
    }
   } break;

   case SkPath::kClose_Verb:
    isClosed = true;
    break;

   case SkPath::kDone_Verb:
    done = true;
    break;
  }
 } while (!done);

 fLength = distance;
 fIsClosed = isClosed;
 fFirstPtIndex = ptIndex;

代碼較長需要慢慢思考。fIter是一個Iter類型,在SKPath.h中的聲明:

/* Iterate through all of the segments (lines, quadratics, cubics) of
each contours in a path.
The iterator cleans up the segments along the way, removing degenerate
segments and adding close verbs where necessary. When the forceClose
argument is provided, each contour (as defined by a new starting
move command) will be completed with a close verb regardless of the
contour's contents. /

從這個聲明中可以明白Iter的作用是遍歷在path中的每一個contour??匆幌?code>Iter.next()方法:

 Verb next(SkPoint pts[4], bool doConsumeDegerates = true) {
   if (doConsumeDegerates) {
    this->consumeDegenerateSegments();
   }
   return this->doNext(pts);
 }

返回值是一個Verb類型:

enum Verb {
 kMove_Verb,  //!< iter.next returns 1 point
 kLine_Verb,  //!< iter.next returns 2 points
 kQuad_Verb, //!< iter.next returns 3 points
 kConic_Verb, //!< iter.next returns 3 points + iter.conicWeight()
 kCubic_Verb, //!< iter.next returns 4 points
 kClose_Verb, //!< iter.next returns 1 point (contour's moveTo pt)
 kDone_Verb,  //!< iter.next returns 0 points
}

不管是什么類型的Path,它一定是由點組成,如果是直線,則兩個點,貝塞爾曲線則三個點,依次類推。

doNext()方法的代碼就不貼出來了,作用就是判斷contour的類型并把相應(yīng)的點的坐標取出傳給pts[4]

fIter.next()返回kDone_Verb時,一次遍歷結(jié)束。

buildSegments中的循環(huán)正是在做此事,而且從case kLine_Verb模式的distance += d;不難發(fā)現(xiàn)這個length是累加起來的。在舉的例子當中,mTickPath有三個contourmEntryPath,mLeftPath,mRightPath),我們調(diào)用mTickMeasure.getLength()時,首先會累計獲取mEntryPath這個contour的長度。

這就不難解釋為什么mTickMeasure獲取的長度和mEntryPath的一樣了。那么想一想,怎么讓buildSegments()對下一個contour進行操作呢?關(guān)鍵是把fLength置為-1

/** Move to the next contour in the path. Return true if one exists, or false if
 we're done with the path.
*/
bool SkPathMeasure::nextContour() {
 fLength = -1;
 return this->getLength() > 0;
}

native層對應(yīng)的API是PathMeasure.nextContour()

總結(jié)

以上就是Android開發(fā)之自定義CheckBox的全部內(nèi)容,希望本文對大家開發(fā)Android有所幫助。

相關(guān)文章

  • Android 新手入門體驗

    Android 新手入門體驗

    本篇文章小編為大家介紹,Android 新手入門體驗。需要的朋友參考下
    2013-04-04
  • Kotlin全局捕捉協(xié)程異常方法詳解

    Kotlin全局捕捉協(xié)程異常方法詳解

    協(xié)程是互相協(xié)作的程序,協(xié)程是結(jié)構(gòu)化的。如果把Java的異常處理機制,照搬到Kotlin協(xié)程中,一定會遇到很多的坑。Kotlin協(xié)程中的異常主要分兩大類,協(xié)程取消異常(CancellationException) 其他異常
    2022-08-08
  • Android進程運行中權(quán)限被收回導(dǎo)致關(guān)閉的問題解決

    Android進程運行中權(quán)限被收回導(dǎo)致關(guān)閉的問題解決

    在Android開發(fā)中我們可能會遇到這樣的問題,進程還在運行著某些權(quán)限卻被收回了,這就導(dǎo)致進程崩潰被迫關(guān)閉,本篇文章將帶你了解這個問題的發(fā)生與解決方法
    2021-10-10
  • Android加載圖片內(nèi)存溢出問題解決方法

    Android加載圖片內(nèi)存溢出問題解決方法

    這篇文章主要介紹了Android加載圖片內(nèi)存溢出問題解決方法,本文講解使用BitmapFactory.Options解決內(nèi)存溢出問題,需要的朋友可以參考下
    2015-06-06
  • Android開發(fā)之資源文件用法實例總結(jié)

    Android開發(fā)之資源文件用法實例總結(jié)

    這篇文章主要介紹了Android開發(fā)之資源文件用法,結(jié)合實例形式總結(jié)分析了Android開發(fā)過程中針對資源文件的常見操作技巧,需要的朋友可以參考下
    2016-02-02
  • Android實現(xiàn)淘寶商品列表切換效果

    Android實現(xiàn)淘寶商品列表切換效果

    這篇文章主要為大家詳細介紹了Android實現(xiàn)淘寶商品列表切換效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • Android 編程下的計時器代碼

    Android 編程下的計時器代碼

    在安卓 APP 的手機號注冊邏輯中,經(jīng)常會有將激活碼發(fā)送到手機的環(huán)節(jié),這個環(huán)節(jié)中絕大多數(shù)的應(yīng)用考慮到網(wǎng)絡(luò)延遲或服務(wù)器壓力以及短信服務(wù)商的延遲等原因,會給用戶提供一個重新獲取激活碼的按鈕
    2013-08-08
  • 解析Android開發(fā)優(yōu)化之:軟引用與弱引用的應(yīng)用

    解析Android開發(fā)優(yōu)化之:軟引用與弱引用的應(yīng)用

    Java從JDK1.2版本開始,就把對象的引用分為四種級別,從而使程序能更加靈活的控制對象的生命周期。這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用,本篇文章重點介紹一下軟引用和弱引用
    2013-05-05
  • Android獲取移動網(wǎng)絡(luò)信號強度的方法

    Android獲取移動網(wǎng)絡(luò)信號強度的方法

    這篇文章主要介紹了Android獲取移動網(wǎng)絡(luò)信號強度的方法,幫助大家更好的理解和學習使用Android,感興趣的朋友可以了解下
    2021-04-04
  • FragmentTabHost使用方法詳解

    FragmentTabHost使用方法詳解

    這篇文章主要為大家詳細介紹了Android中FragmentTabHost的使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08

最新評論