最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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長度是一樣的,到這里我就很奇怪了??匆幌?code>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);
 }

從注釋來看,獲取的是當(dāng)前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);
}

當(dāng)fLength=-1時我們需要計算,也就是說當(dāng)還沒有執(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)的點的坐標(biāo)取出傳給pts[4]

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

buildSegments中的循環(huán)正是在做此事,而且從case kLine_Verb模式的distance += d;不難發(fā)現(xiàn)這個length是累加起來的。在舉的例子當(dāng)中,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)文章

最新評論

原平市| 丰城市| 延川县| 镇雄县| 襄垣县| 三江| 翁源县| 乐都县| 建德市| 珲春市| 利川市| 莱西市| 楚雄市| 扶绥县| 江口县| 万载县| 黄骅市| 交城县| 长沙市| 广水市| 万载县| 镇康县| 元氏县| 洛浦县| 建阳市| 常宁市| 新源县| 吴川市| 望城县| 江源县| 察雅县| 板桥市| 疏附县| 建德市| 天气| 格尔木市| 鸡东县| 革吉县| 万年县| 五常市| 郁南县|