Android 3D畫廊採用Gallery實現無限迴圈、自動輪播
阿新 • • 發佈:2019-02-06
公司最近有一個需求,是打算做一個輪播圖的展示介面,不過和傳統意義上不同,並非是在手機app的頂部展示幾張定時切換的固定大小寬高的圖片,而是中間長方形,兩邊向裡傾斜,形成對稱感的特殊介面,如下圖:
需要實現功能:無限迴圈,自動跳轉,倒影效果。
(原本的企劃是動畫輪播的時候,下面會呈現一條Listview,裡面會因為展示的不同介面而呈現不同的內容,但是後面放棄了。)
下面開始上程式碼:
MainActivity:
備註:自己根據介面適當設定圖片間距,這樣會呈現不同的展示效果。package com.example.gallery; import com.example.gallery.view.MyGallery; import com.example.gallery.view.ImageUtil; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Gallery.LayoutParams; import android.widget.ImageView; import android.widget.Toast; import java.util.Timer; import java.util.TimerTask; public class MainActivity extends Activity { /** * 圖片資源陣列 */ private int[] imageResIDs; private MyGallery gallery; private int index = 0; private final int AUTOPLAY = 2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageResIDs = new int[]{R.drawable.a00, R.drawable.a01, R.drawable.a02, R.drawable.a03, R.drawable.a04, R.drawable.a05,}; gallery = (MyGallery) findViewById(R.id.mygallery); ImageAdapter adapter = new ImageAdapter(); gallery.setAdapter(adapter); gallery.setSpacing(50); //圖片之間的間距 gallery.setSelection((Integer.MAX_VALUE / 2) - (Integer.MAX_VALUE / 2) % imageResIDs.length); gallery.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { } @Override public void onNothingSelected(AdapterView<?> parent) { } }); // 設定點選事件監聽 gallery.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(MainActivity.this, "當前位置position:"+position+"的圖片被選中了", Toast.LENGTH_SHORT).show(); } }); Timer timer = new Timer(); timer.schedule(task, 3000, 3000); } /** * 定時器,實現自動播放 */ private TimerTask task = new TimerTask() { @Override public void run() { Message message = new Message(); message.what = AUTOPLAY; index = gallery.getSelectedItemPosition(); index++; handler.sendMessage(message); } }; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case AUTOPLAY: gallery.setSelection(index); break; default: break; } } }; public class ImageAdapter extends BaseAdapter { @Override public int getCount() { return Integer.MAX_VALUE;//用於迴圈滾動 } @Override public Object getItem(int position) { if (position >= imageResIDs.length) { position = position % imageResIDs.length; } return position; } @Override public long getItemId(int position) { if (position >= imageResIDs.length) { position = position % imageResIDs.length; } return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView; if (convertView != null) { imageView = (ImageView) convertView; } else { imageView = new ImageView(MainActivity.this); } if (position >= imageResIDs.length) { position = position % imageResIDs.length; } Bitmap bitmap = ImageUtil.getImageBitmap(getResources(), imageResIDs[position]); BitmapDrawable drawable = new BitmapDrawable(bitmap); drawable.setAntiAlias(true); // 消除鋸齒 imageView.setImageDrawable(drawable); LayoutParams params = new LayoutParams(240, 320); imageView.setLayoutParams(params); return imageView; } } }
自定義Gallery:
對圖片進行處理:package com.example.gallery.view; import android.content.Context; import android.graphics.Camera; import android.graphics.Matrix; import android.util.AttributeSet; import android.view.View; import android.view.animation.Transformation; import android.widget.Gallery; import android.widget.ImageView; public class MyGallery extends Gallery { /** Gallery的中心點 */ private int galleryCenterPoint = 0; /** 攝像機物件 */ private Camera camera; public MyGallery(Context context, AttributeSet attrs) { super(context, attrs); // 啟動getChildStaticTransformation setStaticTransformationsEnabled(true); camera = new Camera(); } /** * 當Gallery的寬和高改變時回撥此方法,第一次計算gallery的寬和高時,也會呼叫此方法 */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); galleryCenterPoint = getGalleryCenterPoint(); } /** * 返回gallery的item的子圖形的變換效果 * * @param t * 指定當前item的變換效果 */ @Override protected boolean getChildStaticTransformation(View child, Transformation t) { int viewCenterPoint = getViewCenterPoint(child); // view的中心點 int rotateAngle = 0; // 旋轉角度,預設為0 // 如果view的中心點不等於gallery中心,兩邊圖片需要計算旋轉的角度 if (viewCenterPoint != galleryCenterPoint) { // gallery中心點 - view中心點 = 差值 int diff = galleryCenterPoint - viewCenterPoint; // 差值 / 圖片的寬度 = 比值 float scale = (float) diff / (float) child.getWidth(); // 比值 * 最大旋轉角度 = 最終view的旋轉角度(最大旋轉角度定為50度) rotateAngle = (int) (scale * 50); if (Math.abs(rotateAngle) > 50) {// 當最終旋轉角度 》 最大旋轉角度,要改成50或-50 rotateAngle = rotateAngle > 0 ? 50 : -50; } } // 設定變換效果前,需要把Transformation中的上一個item的變換效果清除 t.clear(); t.setTransformationType(Transformation.TYPE_MATRIX); // 設定變換效果的型別為矩陣型別 startTransformationItem((ImageView) child, rotateAngle, t); return true; } /** * 設定變換的效果 * * @param iv * gallery的item * @param rotateAngle * 旋轉的角度 * @param t * 變換的物件 */ private void startTransformationItem(ImageView iv, int rotateAngle, Transformation t) { camera.save(); // 儲存狀態 int absRotateAngle = Math.abs(rotateAngle); // 1.放大效果(中間的圖片要比兩邊的圖片大) camera.translate(0, 0, 100f); // 給攝像機定位 int zoom = -250 + (absRotateAngle * 2); camera.translate(0, 0, zoom); // 2.透明度(中間的圖片完全顯示,兩邊有一定的透明度) int alpha = (int) (255 - (absRotateAngle * 2.5)); iv.setAlpha(alpha); // 3.旋轉(中間的圖片沒有旋轉角度,只要不在中間的圖片都有旋轉角度) camera.rotateY(rotateAngle); Matrix matrix = t.getMatrix(); // 變換的矩陣,將變換效果新增到矩陣中 camera.getMatrix(matrix); // 把matrix矩陣給camera物件,camera物件會把上面新增的效果轉換成矩陣新增到matrix物件中 matrix.preTranslate(-iv.getWidth() / 2, -iv.getHeight() / 2); // 矩陣前乘 matrix.postTranslate(iv.getWidth() / 2, iv.getHeight() / 2); // 矩陣後乘 camera.restore(); // 恢復之前儲存的狀態 } /** * 獲取Gallery的中心點 * * @return */ private int getGalleryCenterPoint() { return this.getWidth() / 2; } /** * 獲取item上view的中心點 * * @param v * @return */ private int getViewCenterPoint(View v) { return v.getWidth() / 2 + v.getLeft(); // 圖片寬度的一半+圖片距離螢幕左邊距 } }
package com.example.gallery.view; import java.lang.ref.SoftReference; import java.util.Hashtable; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Shader.TileMode; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.util.Log; public class ImageUtil { private static final String TAG = "ImageUtil"; /** 快取集合 */ private static Hashtable<Integer, SoftReference<Bitmap>> mImageCache = new Hashtable<Integer, SoftReference<Bitmap>>(); /** * 根據id返回一個處理後的圖片 * * @param res * @param resID * @return */ public static Bitmap getImageBitmap(Resources res, int resID) { // 先去集合中取當前resID是否已經拿過圖片,如果集合中有,說明已經拿過,直接使用集合中的圖片返回 SoftReference<Bitmap> reference = mImageCache.get(resID); if (reference != null) { Bitmap bitmap = reference.get(); if (bitmap != null) {// 從記憶體中取 Log.i(TAG, "從記憶體中取"); return bitmap; } } // 如果集合中沒有,就呼叫getInvertImage得到一個圖片,需要向集合中保留一張,最後返回當前圖片 Log.i(TAG, "重新載入"); Bitmap invertBitmap = getInvertBitmap(res, resID); // 在集合中儲存一份,便於下次獲取時直接在集合中獲取 mImageCache.put(resID, new SoftReference<Bitmap>(invertBitmap)); return invertBitmap; } /** * 根據圖片的id,獲取到處理之後的圖片 * * @param resID * @return */ public static Bitmap getInvertBitmap(Resources res, int resID) { // 1.獲取原圖 Bitmap sourceBitmap = BitmapFactory.decodeResource(res, resID); // 2.生成倒影圖片 Matrix m = new Matrix(); // 圖片矩陣 m.setScale(1.0f, -1.0f); // 讓圖片按照矩陣進行反轉 Bitmap invertBitmap = Bitmap.createBitmap(sourceBitmap, 0, sourceBitmap.getHeight() / 2, sourceBitmap.getWidth(), sourceBitmap.getHeight() / 2, m, false); // 3.兩張圖片合成一張圖片 Bitmap resultBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), (int) (sourceBitmap.getHeight() * 1.5 + 5), Config.ARGB_8888); Canvas canvas = new Canvas(resultBitmap); // 為合成圖片指定一個畫板 canvas.drawBitmap(sourceBitmap, 0f, 0f, null); // 將原圖片畫在畫布的上方 canvas.drawBitmap(invertBitmap, 0f, sourceBitmap.getHeight() + 5, null); // 將倒影圖片畫在畫布的下方 // 4.新增遮罩效果 Paint paint = new Paint(); // 設定遮罩的顏色,這裡使用的是線性梯度 LinearGradient shader = new LinearGradient(0, sourceBitmap.getHeight() + 5, 0, resultBitmap.getHeight(), 0x70ffffff, 0x00ffffff, TileMode.CLAMP); paint.setShader(shader); // 設定模式為:遮罩,取交集 paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); canvas.drawRect(0, sourceBitmap.getHeight() + 5, sourceBitmap.getWidth(), resultBitmap.getHeight(), paint); return resultBitmap; } }
佈局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black">
<com.example.gallery.view.MyGallery
android:id="@+id/mygallery"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</com.example.gallery.view.MyGallery>
</RelativeLayout>
~Demo下載連結~