Android Viewどこにもいない画像ビューア~不服なら見て


オリジナル転載を尊重して明記してください:http://blog.csdn.net/bfbx5173
C.C.    町楼
タイトルから見ると老朽化した機能かもしれませんが、注文した以上後悔しないに違いありません.くだらないことは言わないで、まず効果を見てください.
皆さんはこの効果を見て、手触りがもっと良いと感じたかどうか、そして最初から見てから退出して自然な感じがしたかどうかを見ました.
興味があれば読み続けましょう.
分析:
1、以上が全体の移行の流れであり、同時に背景には徐々に黒くなり、徐々に還元される(透明度のグラデーションの問題ではない).
本来のビューの位置は、viewのapi【getLocationOnScreen】で入手できますが、到着予定の位置は画面の真ん中ではないでしょうか.
2、画像の操作:実はやはり画像にユーザーの指の各种のなでることに応えさせて、1つの指の时はどのようにして、2つの指の时はどのようにします.
もちろんここでは1本の指の時にドラッグを実現し、2本の指の時にスケールを実現します.
では全体の流れが整理され、実現策の中にも数がある.では、2つ目のポイントから始めましょう.
図に示すように、古いポリシーが使用されます.古い戦略とは何かと聞かれたら見てください Android Viewはタッチ操作に視覚的なヒントを与えるのが王道です
このビジュアルヒントをタッチした文章では、 ShinyLayoutとShinyView.PictureLayoutとPictureViewです.
PictureLayoutで各種タッチイベントを受信し,PictureView処理にイベントを渡す.So~
public class PictureLayout extends FrameLayout {
	private PictureView viewPicture;

	public PictureLayout(Context context) {
		this(context, null);
	}

	public PictureLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		setBackgroundColor(0xff000000);
		viewPicture = new PictureView(context);
		viewPicture.setScaleType(ScaleType.CENTER);
		addView(viewPicture);
	}
    /**          */
	@Override public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			return true;
		}
		return super.onTouchEvent(event);
	}
	/**           viewPicture   */
	@Override public boolean dispatchTouchEvent(MotionEvent ev) {
		viewPicture.dealTouchEvent(ev);
		return super.dispatchTouchEvent(ev);
	}
}

その後、PictureViewがこれらの撫でるイベントをどのように処理しているかを見てみましょう.
public class PictureView extends ImageView {
	/**             */
	private int imageWidth, imageHeight;
	/**  */
	private int screenWidth, screenHeight;
	/** 
	 *                 ,          。
* , */ private int scaleRatioReferWidth; /** , , */ private static final float MIN_MIN = 0.5f; /** */ private static final float MIN_NORMAL = 1f; /** , , */ private static final float MAX_MAX = 7f; /** */ private static final float MAX_NORMAL = 6f; private float downX, downY; /** */ private double downDist; /** , */ private float oldScaleX, oldScaleY; /** , ACTION_DOWN */ private float oldTranslationX, oldTranslationY; /** , ~*/ private float fingersCenterX, fingersCenterY; /** , */ private boolean scaleMode; /** */ private boolean anim; public PictureView(Context context) { this(context, null); } public PictureView(Context context, AttributeSet attrs) { super(context, attrs); screenWidth = BaseUtil.getScreenWidth(); screenHeight = BaseUtil.getScreenHeight(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); LogUtil.e("PictureView onSizeChanged width:" + w + ",height:" + h); scaleRatioReferWidth = w / 10; } @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); LogUtil.e("PictureView setImageBitmap width:" + bm.getWidth() + ",height:" + bm.getHeight()); imageWidth = bm.getWidth(); imageHeight = bm.getHeight(); } /** PictureLayout */ public void dealTouchEvent(MotionEvent event) { if (!anim) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // downX = event.getRawX(); downY = event.getRawY(); oldTranslationX = getTranslationX(); oldTranslationY = getTranslationY(); oldScaleX = getScaleX(); oldScaleY = getScaleY(); case MotionEvent.ACTION_MOVE: // if (event.getPointerCount() == 2) { scaleMode = true; // if (downDist == 0) { downDist = calcDistanceBetweenFingers(event); downX = event.getRawX(); downY = event.getRawY(); oldTranslationX = getTranslationX(); oldTranslationY = getTranslationY(); oldScaleX = getScaleX(); oldScaleY = getScaleY(); } // , , 。 ~ double moveDist = calcDistanceBetweenFingers(event); if (moveDist != downDist) { LogUtil.e("moveDist:" +moveDist +"downDist:" + downDist); // , float changedScaleSize = (float) ((moveDist - downDist) / scaleRatioReferWidth); scalePicture(changedScaleSize); } // if (fingersCenterX == 0 && fingersCenterX == 0) { float[] downResult = calcCenterPointBetweenFingers(event); fingersCenterX = downResult[0]; fingersCenterY = downResult[1]; } // [ ] , float[] moveResult = calcCenterPointBetweenFingers(event); if (fingersCenterX != moveResult[0] && fingersCenterY != moveResult[1]) { final float moveX = moveResult[0] - fingersCenterX; final float moveY = moveResult[1] - fingersCenterY; translatePicture(moveX, moveY); } } else if (event.getPointerCount() == 1 && !scaleMode) { // Log.e("Picture", "mvoeX:" + event.getRawX() + ",mvoeX:" + event.getRawY()); final float moveX = event.getRawX() - downX; final float moveY = event.getRawY() - downY; translatePicture(moveX, moveY); } break; case MotionEvent.ACTION_POINTER_UP: break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: // , ~ recoverPicture(); break; } } } /** , */ private void translatePicture(float x, float y) { setTranslationX(oldTranslationX + x / 2); setTranslationY(oldTranslationY + y / 2); } /** , */ private void scalePicture(float changedScaleSize) { if (oldScaleX + changedScaleSize > MIN_MIN && oldScaleX + changedScaleSize < MAX_MAX) { setScaleX(oldScaleX + changedScaleSize); } if (oldScaleY + changedScaleSize > MIN_MIN && oldScaleY + changedScaleSize < MAX_MAX) { setScaleY(oldScaleY + changedScaleSize); } } /** */ private void recoverPicture() { downX = 0; downY = 0; downDist = 0; scaleMode = false; fingersCenterX = 0; fingersCenterY = 0; // , “ ” // , , , 。 // , BUG , OOM~ final float scaleX = getScaleX(); final float scaleY = getScaleY(); final float translationX = getTranslationX(); final float translationY = getTranslationY(); final float transOffsetX = (imageWidth * scaleX - screenWidth) / 2; // X ObjectAnimator anim1 = null; if (transOffsetX < 0) { anim1 = ObjectAnimator.ofFloat(this, "translationX", translationX, 0); } else if (translationX > transOffsetX) { anim1 = ObjectAnimator.ofFloat(this, "translationX", translationX, transOffsetX); } else if (translationX < -transOffsetX) { anim1 = ObjectAnimator.ofFloat(this, "translationX", translationX, -transOffsetX); } // Y final float transOffsetY = (imageHeight * scaleY - screenHeight) / 2; ObjectAnimator anim2 = null; if (transOffsetY < 0) { anim2 = ObjectAnimator.ofFloat(this, "translationY", translationY, 0); } else if (translationY > transOffsetY) { anim2 = ObjectAnimator.ofFloat(this, "translationY", translationY, transOffsetY); } else if (translationY < -transOffsetY) { anim2 = ObjectAnimator.ofFloat(this, "translationY", translationY, -transOffsetY); } // ObjectAnimator anim3 = null; if (scaleX < MIN_NORMAL) { anim3 = ObjectAnimator.ofFloat(this, "scaleX", scaleX, MIN_NORMAL); } else if (scaleX > MAX_NORMAL) { anim3 = ObjectAnimator.ofFloat(this, "scaleX", scaleX, MAX_NORMAL); } // ObjectAnimator anim4 = null; if (scaleY < MIN_NORMAL) { anim4 = ObjectAnimator.ofFloat(this, "scaleY", scaleY, MIN_NORMAL); } else if (scaleY > MAX_NORMAL) { anim4 = ObjectAnimator.ofFloat(this, "scaleY", scaleY, MAX_NORMAL); } AnimatorSet set = new AnimatorSet(); AnimatorSet.Builder b = set.play(ObjectAnimator.ofInt(0)); if (anim1 != null) b.with(anim1); if (anim2 != null) b.with(anim2); if (anim3 != null) b.with(anim3); if (anim4 != null) b.with(anim4); set.setInterpolator(new DecelerateInterpolator()); set.setDuration(400); set.start(); set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); anim = true; } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); anim = false; } }); } /** */ private double calcDistanceBetweenFingers(MotionEvent event) { float disX = Math.abs(event.getX(0) - event.getX(1)); float disY = Math.abs(event.getY(0) - event.getY(1)); return Math.sqrt(disX * disX + disY * disY); } /** */ private float[] calcCenterPointBetweenFingers(MotionEvent event) { float xPoint0 = event.getX(0); float yPoint0 = event.getY(0); float xPoint1 = event.getX(1); float yPoint1 = event.getY(1); return new float[] { (xPoint0 + xPoint1) / 2, (yPoint0 + yPoint1) / 2 }; } }

PictureLayoutから PictureView呼び出しの方法は 
dealTouchEvent、見てみましょう.
ACTION_DOWNに記録された初期の状態.ACTION_MOVEでは event.getPointerCount()は、単指操作か双指操作かを判断し、論理を重ねると2つのメソッドが呼び出されます. 义齿 scalePicture. この2つの方法では、移動が計算されたいくつかの数値に基づいて、Viewの元のAPIを呼び出してビューのプロパティを変更し、所望の表示効果を達成する.最後にACTION_UPでは,recoverPictureを直接呼び出して何らかのものを復元して後始末を行う.
では、その後、移行したタスクを完了します.PictureLayoutでは、
public class PictureLayout extends FrameLayout {

	//       
	
	private PictureView viewPicture;
	private int originalX, originalY, centerX, centerY;
	private boolean finishAnim;

	/**      */
	public void showPicture(String url, View original) {
		if (TextUtils.isEmpty(url)) throw new IllegalArgumentException("empty img url");

		//          
		int[] location = new int[2];
		original.getLocationOnScreen(location);
		originalX = location[0];
		originalY = location[1] - BaseUtil.getStatusBarHeight();

		setVisibility(View.VISIBLE);
                //     
		App.i()
				.getImageLoader()
				.get(url,
						ImageLoader.getImageListener(viewPicture, R.drawable.test_img_default, R.drawable.test_img_loading,
								R.drawable.test_img_fail));

		centerX = (BaseUtil.getScreenWidth()) / 2;
		centerY = (BaseUtil.getScreenHeight()) / 2;

		LogUtil.d("centerX:" + centerX + ",centerY:" + centerY);

		//  PictureView                     
		if (originalX != 0 || originalY != 0) {
			viewPicture.setTranslationX(originalX - centerX);
			viewPicture.setTranslationY(originalY - centerY);
		}
		//         ,    ,   ,   ~  PictureView     
		ObjectAnimator anim1 = ObjectAnimator.ofFloat(viewPicture, "translationX", originalX - centerX, 0);
		ObjectAnimator anim2 = ObjectAnimator.ofFloat(viewPicture, "translationY", originalY - centerY, 0);
		ObjectAnimator anim3 = ObjectAnimator.ofFloat(viewPicture, "scaleX", 1, 2);
		ObjectAnimator anim4 = ObjectAnimator.ofFloat(viewPicture, "scaleY", 1, 2);
		ObjectAnimator anim5 = ObjectAnimator.ofFloat(viewPicture, "rotation", 0, 360);
		ObjectAnimator anim6 = ObjectAnimator.ofFloat(this, "alpha", 0, 1);

		AnimatorSet set = new AnimatorSet();
		set.play(anim1).with(anim2).with(anim3).with(anim4).with(anim5).with(anim6);
		set.setInterpolator(new DecelerateInterpolator());
		set.setDuration(600);
		set.start();
		set.addListener(new AnimatorListenerAdapter() {
			@Override public void onAnimationEnd(Animator animation) {
				super.onAnimationEnd(animation);
			}
		});

	}
	/**      */
	public void hiddenPictrue() {
		if (finishAnim) return;
		finishAnim = true;
		
		//           
		ObjectAnimator anim1 = ObjectAnimator.ofFloat(viewPicture, "translationX", 0, originalX - centerX);
		ObjectAnimator anim2 = ObjectAnimator.ofFloat(viewPicture, "translationY", 0, originalY - centerY);
		ObjectAnimator anim3 = ObjectAnimator.ofFloat(viewPicture, "scaleX", viewPicture.getScaleX(), 1);
		ObjectAnimator anim4 = ObjectAnimator.ofFloat(viewPicture, "scaleY", viewPicture.getScaleY(), 1);
		ObjectAnimator anim5 = ObjectAnimator.ofFloat(viewPicture, "rotation", 360, 0);
		ObjectAnimator anim6 = ObjectAnimator.ofFloat(this, "alpha", 1, 0);
		anim5.setInterpolator(new DecelerateInterpolator());

		AnimatorSet set = new AnimatorSet();
		set.play(anim1).with(anim2).with(anim3).with(anim4).with(anim5).with(anim6);
		set.setDuration(600);
		set.start();
		set.addListener(new AnimatorListenerAdapter() {
			@Override public void onAnimationEnd(Animator animation) {
				super.onAnimationEnd(animation);
				setVisibility(View.GONE);
				finishAnim = false;
			}
		});
	}
}

2つの公開方法showPictureとhiddenPictureしかないので、方法名を見れば意味がわかると信じています.
彼らの内容は、最初の分析に基づいて、2つの点を得た後(元のビューの始点、画像の中心点を示す)にアニメーションの移行を行います.
次はタイトルのようにどこにもいないようにします.まだその言叶、前编は私达はすでに轨迹の点をどこにもいないことを譲って、だから
BaseActivityで
@Override public void setContentView(View view, ViewGroup.LayoutParams params) {
		layContentWrapper = new ShinyLayout(this);
		layContentWrapper.setDispatchTouchEventListener(new ShinyLayout.DispatchTouchEventListener() {
			@Override public void onDispatchTouchEvent(MotionEvent ev) {
				switch (ev.getAction()) {
				case MotionEvent.ACTION_UP:
					hideSoftInputFromWindow();
					break;
				}
			}
		});
		// layContentWrapper     ~
		//     PictureLayout       。           ~  。        
		layPicture = new PictureLayout(this);
		layPicture.setVisibility(View.GONE);
		layContentWrapper.addView(layPicture, 0);

		layContentWrapper.addView(view, 0);

		if (params != null) {
			super.setContentView(layContentWrapper, params);
		}
		else {
			super.setContentView(layContentWrapper);
		}
	}

注釈が完璧だと感じたら、説明をしません.このようにBaseActivityを継承したアクティビティは簡単に呼び出せばよい.
final String testImageUrl2 = "https://img-my.csdn.net/uploads/avatar/5/1/A/1_bfbx5173.jpg";
		final ImageView imgTest2 = (ImageView) findViewById(R.id.img_test2);
		imgTest2.setType(ImageView.TYPE_OVAL);
		imgTest2.loadImage(testImageUrl2);
		imgTest2.setOnClickListener(new View.OnClickListener() {
			@Override public void onClick(View v) {
				layPicture.showPicture(testImageUrl2, v);
			}
		});

見たかどうかは、1つの画像のurlアドレスとクリックされたview自体だけでいい!!
また、hiddenPictureメソッドを使用して、戻りキーのオフをサポートするために、BsseActivityを再構築する方法を継続します.
@Override public final void onBackPressed() {
		if (View.VISIBLE == layPicture.getVisibility()) {
			layPicture.hiddenPictrue();
		}
		else if (!onChildBackPressed()) {
			super.onBackPressed();
		}
	}

	protected  boolean onChildBackPressed() {
		// child impl
		return false;
	}

ここでは、onBackPressedにfinalキーワード修飾が付けられているので、サブクラスは継承できません.なぜこのように書くのかというと、コード実行順序(図を見た状態かどうかを最初に判断するには、hiddenが先に、その後がサブクラスactivityのonBackPressedである.
最後にもう一度効果を楽しみましょう~
PictureLayoutソースを確認してください
見てくれてありがとう~