안 드 로 이 드 프리미엄 소니 스크롤 앨범

13675 단어 Android앨범
소니 의 휴대 전 화 는 잘 팔 리 지 않 지만 어떤 것들 은 잘 만 들 어 졌 다.산업 디자인 은 말 할 것 도 없고 소니 의 앨범 의 두 손가락 임 의 크기 조정 기능 도 특히 시 크 하 다.그 데스크 톱 의 작은 부품 이 앨범 을 굴 리 는 것 도 좋 을 것 같 습 니 다.구 글 의 원생 앨범 벽 보다 기능 이 훨씬 좋 습 니 다.인터넷 에서 검색 해 보 았 지만 이 글 을 쓰 는 사람 이 있 는 것 을 발견 하지 못 했 습 니 다.그래서 다음은 제 고 A 상품 을 소개 하 겠 습 니 다. 
우선 효과 그림:
 
주요 제스처 동작 은 다음 과 같 습 니 다.
 1.위/아래 만 속 이동,한 장의 그림 을 위/아래로 미 끄 러 뜨 릴 수 있 습 니 다.
 2.위/아래 빠르게 읽 고 이동 하면 미끄럼 속도 에 따라 위/아래 여러 장의 그림 이 미 끄 러 집 니 다.
 3.클릭 하면 시스템 갤러리 에 이 그림 을 보 여 달라 고 요청 합 니 다.
이 작은 위 젯 의 주요 장점 은 화면 안의 작은 범위 에서 좋 은 그림 선택/위 젯 을 제공 합 니 다.특히 그림 을 전환 할 때 애니메이션 감각 을 멀리 하고 호감 을 가 집 니 다. 
코드 분석
처음에 이 작은 부품 을 생각 할 때 여러 개의 ImageView 를 중첩 하여 실현 하 는 효과 라 고 생각 했다.예 를 들 어 구 글 원생 의 이 부품 은 여러 개의 ImageView 를 중첩 하여 형 성 된 것 이지 만 효 과 는 이것 보다 훨씬 못 하 다.하지만 여러 이미지 뷰 를 통 해 중첩 하면 이렇게 원활 하지 않 고 성능 도 좋 지 않 을 것 같다.이 효과 자체 도 비교적 규칙 적 이 므 로 하나의 View 를 통 해 실현 하여 더욱 좋 은 성능 을 얻 을 수 있 을 것 이다.그래서 View Hierarchy 분석 을 통 해 소니 는 역시 하나의 View 를 통 해 이 루어 졌 기 때문에 다음 과 같은 방식 으로 이 작은 부품 을 분석 했다.
코드 는 주로 세 부분 으로 구성 된다.
 •RollImageView:실제 View
 •CellCalculator:모든 그림 의 그리 기 영역 과 투명 도 를 실시 간 으로 계산 합 니 다.이것 은 이 작은 부품 의 핵심 부품 입 니 다.인터페이스 정 의 는 다음 과 같 습 니 다. 

/**
  * get all rects for drawing image
  * @return
  */
 public Cell[] getCells();

 /**
  *
  * @param distance the motion distance during the period from ACTION_DOWN to this moment
  * @return 0 means no roll, positive number means roll forward and negative means roll backward
  */
 public int setStatus(float distance);


 /**
  * set the dimen of view
  * @param widht
  * @param height
  */
 public void setDimen(int widht, int height);

 /**
  * set to the status for static
  */
 public void setStatic();

•ImageLoader:그림 을 불 러 오고 Bitmap 를 제공 하여 RollImageView 에 그립 니 다.인터페이스 정 의 는 다음 과 같 습 니 다. 

/**
  * the images shown roll forward
  */
 public void rollForward();

 /**
  * the images shown roll backward
  */
 public void rollBackward();

 /**
  * get bitmaps
  * @return
  */
 public Bitmap[] getBitmap();

 /**
  * use invalidate to invalidate the view
  * @param invalidate
  */
 public void setInvalidate(RollImageView.InvalidateView invalidate);

 /**
  * set the dimen of view
  * @param width
  * @param height
  */
 public void setDimen(int width, int height);


 /**
  * the image path to be show
  * @param paths
  */
 public void setImagePaths(List<String> paths);

 /**
  * get large bitmap while static
  */
 public void loadCurrentLargeBitmap(); 

다음은 각 부분의 핵심 코드 를 분석 하 겠 습 니 다. 
RollImageView
View 의 주요 직책 은 draw 각 bitmap 와 사용자 의 제스처 동작 에 응답 하 는 것 으로 상대 적 으로 간단 합 니 다.
그리 기 부분 은 ImageLoader 에서 얻 은 각 Bitmap 를 CellCalculater 에서 얻 은 그리 기 영역 과 투명도 에 따라 화면 에 그 리 는 것 입 니 다.현재 이 코드 는 비교적 간단 합 니 다.서로 다른 사이즈 의 그림 을 고려 하지 않 고 더욱 조 화 롭 게 표시 해 야 합 니 다.예 를 들 어 ImageView.Scale Type 에서 정의 한 디 스 플레이 방식 입 니 다.   

@Override
 public void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  Bitmap[] bitmaps = mImageLoader.getBitmap();
  Cell[] cells = mCellCalculator.getCells(); //    Image         
  canvas.translate(getWidth() / 2, 0);
  for (int i = SHOW_CNT - 1; i >= 0; i--) { //     Image    
   Bitmap bitmap = bitmaps[i];
   Cell cell = cells[i];
   if (bitmap != null && !bitmap.isRecycled()) {
    mPaint.setAlpha(cell.getAlpha());
    LOG("ondraw " + i + bitmap.getWidth() + " " + cell.getRectF() + " alpha " + cell.getAlpha());
    canvas.drawBitmap(bitmap, null, cell.getRectF(), mPaint);
   }
  }
 }

제스처 부분 은 GestureListener 를 사 용 했 습 니 다.주요 코드 는 다음 과 같 습 니 다. 

@Override
 public boolean onTouchEvent(MotionEvent event) {
  if (event.getPointerCount() > 1) {
   return false;
  }
  mGestureDetector.onTouchEvent(event);
  switch (event.getAction()) {
   case MotionEvent.ACTION_UP: //            Fling   ,            
    if(!mIsFling){
     if(mRollResult == CellCalculator.ROLL_FORWARD){
      mImageLoader.rollForward();
     } else if (mRollResult == CellCalculator.ROLL_BACKWARD && !mScrollRollBack){
      mImageLoader.rollBackward();
     }
     LOG("OnGestureListener ACTION_UP setstatic " );
     mCellCalculator.setStatic();
     mImageLoader.loadCurrentLargeBitmap();
    }
    break;
   default:
    break;
  }
  return true;
 }

 
 //    
 @Override
 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
  mScrollDistance += distanceY;
  if(mScrollDistance > 0 && !mScrollRollBack){
   mImageLoader.rollBackward();
   mScrollRollBack = true;
  } else if(mScrollDistance < 0 && mScrollRollBack){
   mImageLoader.rollForward();
   mScrollRollBack = false;
  }
  LOG("OnGestureListener onScroll " + distanceY + " all" + mScrollDistance);
  mRollResult = mCellCalculator.setStatus(-mScrollDistance);
  invalidate();
  return true;
 }
 
 //    
 @Override
 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
  if (Math.abs(velocityY) > MIN_FLING) {
   LOG("OnGestureListener onFling " + velocityY);
   if (mExecutorService == null) {
    mExecutorService = Executors.newSingleThreadExecutor();
   }
   mIsFling = true;
   mExecutorService.submit(new FlingTask(velocityY));
  }
  return true;
 }
 
 //               Images
 private class FlingTask implements Runnable {

  float mVelocity;
  float mViewHeight;
  int mSleepTime;
  boolean mRollBackward;

  FlingTask(float velocity) {
   mRollBackward = velocity < 0 ? true : false;
   mVelocity = Math.abs(velocity / 4);
   mViewHeight = RollImageView.this.getHeight() / 2;
   mSleepTime = (int)(4000 / Math.abs(velocity) * 100); //the slower velocity of fling, the longer interval for roll
  }

  @Override
  public void run() {
   int i = 0;
   try{
    while (mVelocity > mViewHeight) {
     mCellCalculator.setStatus(mRollBackward ? -mViewHeight : mViewHeight);
     mHandler.sendEmptyMessage(MSG_INVALATE);
     //determines the count of roll. The using of mViewHeight has no strictly logical
     mVelocity -= mViewHeight;
     if (((i++) & 1) == 0) { //roll forward once for every two setStatus
      if(mRollBackward){
       mImageLoader.rollBackward();
      }else {
       mImageLoader.rollForward();
      }
     }
     Thread.sleep(mSleepTime);
    }
    mCellCalculator.setStatic();
    mImageLoader.loadCurrentLargeBitmap();
    mHandler.sendEmptyMessage(MSG_INVALATE);
   } catch(Exception e){

   } finally{

   }
  }
 }

셀 계산기 분석
먼저 앞으로 이동/뒤로 이동 하 는 개념 을 천명 하 다.표시 할 그림 경 로 는 하나의 List 로 저장 되 어 있 습 니 다.맨 앞 에 표 시 된 그림 의 색인 이 index 라 고 가정 하면 현재 표 시 된 그림 은[index,index+3]이 고 앞 으로 는 index 플러스 1 을 표시 하 며 뒤로 는 index 감 1 을 표시 합 니 다.
CellCalculator 의 계산 상황 은 주로 사용자 가 손짓 을 통 해 그림 을 앞으로 이동 하거나 뒤로 이동 해 야 한 다 는 의 도 를 나 타 냈 다.View 에서 얻 을 수 있 는 것 은 제스처 로 이동 하 는 거리 이기 때문에 CellCalculater 에서 들 어 오 는 이동 거 리 를 처리 하고 이동 결 과 를 출력 해 야 합 니 다.나의 실현 에서 이동 거리 가 그림 높이 의 절반 을 초과 할 때 표 시 된 그림 은 한 자 리 를 이동 해 야 한 다 는 것 을 나타 낸다.그렇지 않 으 면 제스처 조작 이 끝 날 때 static 상태 로 설정 된다.주요 코드 는 다음 과 같 습 니 다. 

public DefaultCellCalculator(int showCnt){
  mCnt = showCnt;
  mCells = new Cell[mCnt];
  mAlphas = new float[mCnt];
  STATIC_ALPHA = new int[mCnt];
  STATIC_ALPHA[mCnt - 1] = 0; //          0
  int alphaUnit = (255 - FIRST_ALPHA) / (mCnt - 2);
  for(int i = mCnt - 2; i >= 0; i--){ //            
   STATIC_ALPHA[i] = FIRST_ALPHA + (mCnt - 2 - i) * alphaUnit;
  }
 }

 @Override
 public Cell[] getCells() {
  return mCells;
 }
 
 //      ,distance      ,            /    
 @Override
 public int setStatus(float distance) {
  if(distance > 0){
   return calculateForward(distance);
  } else if(distance < 0){
   return calculateBackward(distance);
  } else{
   initCells();
  }
  return 0;
 }

 //  RollImageView   ,           
 @Override
 public void setDimen(int widht, int height) {
  mViewWidth = widht;
  mViewHeight = height;
  mWidhtIndent = (int)(WIDHT_INDENT * mViewWidth);
  mWidths = new int[mCnt];
  for(int i = 0; i < mCnt; i++){
   mWidths[i] = mViewWidth - i * mWidhtIndent;
  }
  //       。
  //       ,             ,             ,   mcnt-1
  mImageHeight = mViewHeight - (mCnt - 1) * HEIGHT_INDENT;
  LOG("mImageHeight " + mImageHeight);
  initCells();
 }

 
 //   ,          
 @Override
 public void setStatic() {
  initCells();
 }

 //              
 private int calculateForward(float status){
  float scale = status / mImageHeight;
  LOG("scale " + scale + " mImageHeight " + mImageHeight + " status " + status);
  for(int i = 1; i < mCnt; i++){
   mCells[i].setWidth(interpolate(scale * 3, mWidths[i], mWidths[i - 1])); // *3            ,   
   mCells[i].moveVertical(interpolate(scale * 10, 0, HEIGHT_INDENT)); //*10           ,        
   mCells[i].setAlpha((int)interpolate(scale, STATIC_ALPHA[i], STATIC_ALPHA[i - 1]));
  }
  mCells[0].moveVertical(status);
  mCells[0].setAlpha((int)interpolate(scale, 255, 0));
  if(status >= mImageHeight / 3){
   return ROLL_FORWARD;
  } else {
   return 0;
  }
 }

 //              
 private int calculateBackward(float status){
  float scale = Math.abs(status / mImageHeight);
  for(int i = 1; i < mCnt; i++){
   mCells[i].setWidth(interpolate(scale, mWidths[i - 1], mWidths[i]));
   mCells[i].moveVertical(-scale * HEIGHT_INDENT);
   mCells[i].setAlpha((int)interpolate(scale, STATIC_ALPHA[i - 1], STATIC_ALPHA[i]));
  }
  mCells[0].resetRect();
  mCells[0].setWidth(mWidths[0]);
  mCells[0].setHeight(mImageHeight);
  mCells[0].moveVertical(mImageHeight + status);
  mCells[0].setAlpha((int)interpolate(scale, 0, 255));
  if(-status >= mImageHeight / 3){
   return ROLL_BACKWARD;
  } else {
   return 0;
  }
 }

 /**
  * status without move
  */
 private void initCells(){
  int top = -HEIGHT_INDENT;
  for(int i = 0; i < mCnt; i++){
   RectF rectF = new RectF(0,0,0,0);
   rectF.top = top + (mCnt - 1 - i) * HEIGHT_INDENT;
   rectF.bottom = rectF.top + mImageHeight;
   mCells[i] = new Cell(rectF, STATIC_ALPHA[i]);
   mCells[i].setWidth(mWidths[i]);
  }
 }

 //    
 private float interpolate(float scale, float start, float end){
  if(scale > 1){
   scale = 1;
  }
  return start + scale * (end - start);
 }

ImageLoader 분석
ImageLoader 는 사실 비교적 간단 하 다.주로 다음 과 같은 두 가지 가 있다.
 •제스처 동작 에 응답 하여 앞으로/뒤로 이동 할 때 Bitmap 요청 처리
 •제스처 가 아직 조작 되 고 있 을 때 작은 그림 을 불 러 오고 제스처 작업 이 끝 난 후에 큰 그림 을 불 러 와 야 합 니 다.천천히 이동 할 때 만 선명 하 게 표시 되 고 빠르게 이동 할 때 작은 그림 을 표시 하면 되 기 때문에 현재 index 와 앞으로 그림 을 불 러 오 면 됩 니 다.

//    index          
 @Override
 public void loadCurrentLargeBitmap() {
  for(int i = mCurrentIndex - 1; i < mCurrentIndex + 2; i++){
   if(i >= 0 && i < mImagesCnt - 1){
    mBitmapCache.getLargeBitmap(mAllImagePaths[i]);
   }
  }
 }

 //index      
 @Override
 public void rollForward() {
  LOG("rollForward");
  mCurrentIndex++;
  if(mCurrentIndex > mImagesCnt - 1){
   mCurrentIndex = mImagesCnt - 1;
  }
  setCurrentPaths();
 }

 //index      
 @Override
 public void rollBackward() {
  LOG("rollBackward");
  mCurrentIndex--;
  if(mCurrentIndex < 0){
   mCurrentIndex = 0;
  }
  setCurrentPaths();
 }

 @Override
 public Bitmap[] getBitmap() {
  if(mCurrentPaths != null){
   LOG("getBitmap paths nut null");
   for(int i = mCurrentIndex, j = 0; j < mShowCnt; j++, i++){
    if(i >= 0 && i < mImagesCnt){
     mCurrentBitmaps[j] = mBitmapCache.getBimap(mAllImagePaths[i]);
    } else{
     mCurrentBitmaps[j] = mBitmapCache.getBimap(NO_PATH);
    }
   }
  }
  return mCurrentBitmaps;
 }

마지막 으로 모든 소스 코드:https://github.com/willhua/RollImage
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기