Androidプログラミングが実現された、超ヒョンさんの画像ブラウザ。

13818 ワード

本論文の例は、Androidプログラミングが実現される、超ヒョンジュン画像ブラウザについて述べる。皆さんに参考にしてあげます。具体的には以下の通りです。
Androidのギャラリー・コンポーネントを使ったことがある人は知っていますが、ギャラリはドラッグして一セットの画像を見る効果があります。iphoneよりもドラッグしてブラウズするための画像のCoverflowです。明らかに見劣りがします。実際には、ギャラリーを拡張することで、擬似3 D変換により、coverflowの効果がほぼ実現できる。本論文はソースコード解析という機能によって実現される。具体的なコードの役割はコメントを参照することができます。
最終的な達成効果は以下の通りです。
ギャラリーを使うには、まずアドホッターを指定しなければなりません。ここでは、画像のイメージダウン効果をカスタマイズしました。
着信パラメータはcontextとプログラム内のdrawableの中のピクチャID配列です。その後、その中のcreateReflectImages()を呼び出して、それぞれの画像の倒影効果を創造し、対応するImageView配列を生成し、最後にgetView()に戻ります。

Copyright (C) 2010 Neil Davies 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* [url]http://www.apache.org/licenses/LICENSE-2.0[/url] 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
* 
* This code is base on the Android Gallery widget and was Created 
* by Neil Davies neild001 'at' gmail dot com to be a Coverflow widget 
* 
* @author Neil Davies 
*/ 
public class ImageAdapter extends BaseAdapter { 
int mGalleryItemBackground; 
private Context mContext; 
private Integer[] mImageIds ; 
private ImageView[] mImages; 
public ImageAdapter(Context c, int[] ImageIds) { 
mContext = c; 
mImageIds = ImageIds; 
mImages = new ImageView[mImageIds.length]; 
} 
public boolean createReflectedImages() { 
// The gap we want between the reflection and the original image 
final int reflectionGap = 4; 
int index = 0; 
for (int imageId : mImageIds) { 
Bitmap originalImage = BitmapFactory.decodeResource( 
mContext.getResources(), imageId); 
int width = originalImage.getWidth(); 
int height = originalImage.getHeight(); 
// This will not scale but will flip on the Y axis 
Matrix matrix = new Matrix(); 
matrix.preScale(1, -1); 
// Create a Bitmap with the flip matrix applied to it. 
// We only want the bottom half of the image 
Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, 
height / 2, width, height / 2, matrix, false); 
// Create a new bitmap with same width but taller to fit 
// reflection 
Bitmap bitmapWithReflection = Bitmap.createBitmap(width, 
(height + height / 2), Config.ARGB_8888); 
// Create a new Canvas with the bitmap that's big enough for 
// the image plus gap plus reflection 
Canvas canvas = new Canvas(bitmapWithReflection); 
// Draw in the original image 
canvas.drawBitmap(originalImage, 0, 0, null); 
// Draw in the gap 
Paint deafaultPaint = new Paint(); 
canvas.drawRect(0, height, width, height + reflectionGap, 
deafaultPaint); 
// Draw in the reflection 
canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, 
null); 
// Create a shader that is a linear gradient that covers the 
// reflection 
Paint paint = new Paint(); 
LinearGradient shader = new LinearGradient(0, 
originalImage.getHeight(), 0, 
bitmapWithReflection.getHeight() + reflectionGap, 
0x70ffffff, 0x00ffffff, TileMode.CLAMP); 
// Set the paint to use this shader (linear gradient) 
paint.setShader(shader); 
// Set the Transfer mode to be porter duff and destination in 
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); 
// Draw a rectangle using the paint with our linear gradient 
canvas.drawRect(0, height, width, 
bitmapWithReflection.getHeight() + reflectionGap, paint); 
ImageView imageView = new ImageView(mContext); 
imageView.setImageBitmap(bitmapWithReflection); 
imageView 
.setLayoutParams(new GalleryFlow.LayoutParams(160, 240)); 
// imageView.setScaleType(ScaleType.MATRIX); 
mImages[index++] = imageView; 
} 
return true; 
} 
public int getCount() { 
return mImageIds.length; 
} 
public Object getItem(int position) { 
return position; 
} 
public long getItemId(int position) { 
return position; 
} 
public View getView(int position, View convertView, ViewGroup parent) { 
// Use this code if you want to load from resources 
/* 
* ImageView i = new ImageView(mContext); 
* i.setImageResource(mImageIds[position]); i.setLayoutParams(new 
* CoverFlow.LayoutParams(350,350)); 
* i.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 
* 
* //Make sure we set anti-aliasing otherwise we get jaggies 
* BitmapDrawable drawable = (BitmapDrawable) i.getDrawable(); 
* drawable.setAntiAlias(true); return i; 
*/ 
return mImages[position]; 
} 
/** 
* Returns the size (0.0f to 1.0f) of the views depending on the 
* 'offset' to the center. 
*/ 
public float getScale(boolean focused, int offset) { 
/* Formula: 1 / (2 ^ offset) */ 
returnMath.max(0,1.0f / (float) Math.pow(2, Math.abs(offset))); 
} 
} 
}

画像の倒影効果を実現しただけでは足りません。coverflowでは画像の切り替えは回転やズーム効果があります。ですから、私たちは自分のギャラリーを拡張して、自分のギャラリーを実現します。オリジナルギャラリークラスでは、画像への変換を実現するための方法get ChildStatic Trans formation()が提供されている。この方法を上書きして、カスタマイズしたtranformImageBitmap(「画像ごとにギャラリーの中心からの距離」)を呼び出すことで、各画像に対応する回転とスケーリングを行うことができます。ここでは、ビュー変換のためにcameraとmatrixが使用されている。具体的にはコードコメントを参照することができます。

public class GalleryFlow extendsGallery { 
  /** 
   * Graphics Camera used for transforming the matrix of ImageViews 
   */ 
  privateCamera mCamera = newCamera(); 
  /** 
   * The maximum angle the Child ImageView will be rotated by 
   */ 
  privateint mMaxRotationAngle =60; 
  /** 
   * The maximum zoom on the centre Child 
   */ 
  privateint mMaxZoom = -120; 
  /** 
   * The Centre of the Coverflow 
   */ 
  privateint mCoveflowCenter; 
  publicGalleryFlow(Context context) { 
    super(context); 
    this.setStaticTransformationsEnabled(true); 
  } 
  publicGalleryFlow(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    this.setStaticTransformationsEnabled(true); 
  } 
  publicGalleryFlow(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 
    this.setStaticTransformationsEnabled(true); 
  } 
  /** 
   * Get the max rotational angle of the image 
   * 
   * @return the mMaxRotationAngle 
   */ 
  publicint getMaxRotationAngle() { 
    returnmMaxRotationAngle; 
  } 
  /** 
   * Set the max rotational angle of each image 
   * 
   * @param maxRotationAngle 
   *   the mMaxRotationAngle to set 
   */ 
  publicvoid setMaxRotationAngle(intmaxRotationAngle) { 
    mMaxRotationAngle = maxRotationAngle; 
  } 
  /** 
   * Get the Max zoom of the centre image 
   * 
   * @return the mMaxZoom 
   */ 
  publicint getMaxZoom() { 
    returnmMaxZoom; 
  } 
  /** 
   * Set the max zoom of the centre image 
   * 
   * @param maxZoom 
   *   the mMaxZoom to set 
   */ 
  publicvoid setMaxZoom(intmaxZoom) { 
    mMaxZoom = maxZoom; 
  } 
  /** 
   * Get the Centre of the Coverflow 
   * 
   * @return The centre of this Coverflow. 
   */ 
  privateint getCenterOfCoverflow() { 
    return(getWidth() - getPaddingLeft() - getPaddingRight()) / 2 
        + getPaddingLeft(); 
  } 
  /** 
   * Get the Centre of the View 
   * 
   * @return The centre of the given view. 
   */ 
  privatestatic int getCenterOfView(View view) { 
    returnview.getLeft() + view.getWidth() / 2; 
  } 
  /** 
   * {@inheritDoc} 
   * 
   * @see #setStaticTransformationsEnabled(boolean) 
   */ 
  protectedboolean getChildStaticTransformation(View child, Transformation t) { 
    finalint childCenter = getCenterOfView(child); 
    finalint childWidth = child.getWidth(); 
    introtationAngle = 0; 
    t.clear(); 
    t.setTransformationType(Transformation.TYPE_MATRIX); 
    if(childCenter == mCoveflowCenter) { 
      transformImageBitmap((ImageView) child, t,0); 
    }else { 
      rotationAngle = (int) (((float) (mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle); 
      if(Math.abs(rotationAngle) > mMaxRotationAngle) { 
        rotationAngle = (rotationAngle <0) ? -mMaxRotationAngle 
            : mMaxRotationAngle; 
      } 
      transformImageBitmap((ImageView) child, t, rotationAngle); 
    } 
    returntrue; 
  } 
  /** 
   * This is called during layout when the size of this view has changed. If 
   * you were just added to the view hierarchy, you're called with the old 
   * values of 0. 
   * 
   * @param w 
   *   Current width of this view. 
   * @param h 
   *   Current height of this view. 
   * @param oldw 
   *   Old width of this view. 
   * @param oldh 
   *   Old height of this view. 
   */ 
  protectedvoid onSizeChanged(intw, int h, int oldw, int oldh) { 
    mCoveflowCenter = getCenterOfCoverflow(); 
    super.onSizeChanged(w, h, oldw, oldh); 
  } 
  /** 
   * Transform the Image Bitmap by the Angle passed 
   * 
   * @param imageView 
   *   ImageView the ImageView whose bitmap we want to rotate 
   * @param t 
   *   transformation 
   * @param rotationAngle 
   *   the Angle by which to rotate the Bitmap 
   */ 
  privatevoid transformImageBitmap(ImageView child, Transformation t, 
      introtationAngle) { 
    mCamera.save(); 
    finalMatrix imageMatrix = t.getMatrix(); 
    finalint imageHeight = child.getLayoutParams().height; 
    finalint imageWidth = child.getLayoutParams().width; 
    finalint rotation = Math.abs(rotationAngle); 
    //  Z      camera   ,         。 
    //    Y    ,       ;X          。 
    mCamera.translate(0.0f,0.0f, 100.0f); 
    // As the angle of the view gets less, zoom in 
    if(rotation < mMaxRotationAngle) { 
      floatzoomAmount = (float) (mMaxZoom + (rotation *1.5)); 
      mCamera.translate(0.0f,0.0f, zoomAmount); 
    } 
    //  Y    ,          。 
    //    X    ,           。 
    mCamera.rotateY(rotationAngle); 
    mCamera.getMatrix(imageMatrix); 
    imageMatrix.preTranslate(-(imageWidth /2), -(imageHeight /2)); 
    imageMatrix.postTranslate((imageWidth /2), (imageHeight /2)); 
    mCamera.restore(); 
  } 
}

コードはここで終わります。興味があれば、中のパラメータを自分で調整してもっと華やかな効果を実現できます。
以下は呼び出しの例です。

public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.layout_gallery); 
    Integer[] images = { R.drawable.img0001, R.drawable.img0030, 
      R.drawable.img0100, R.drawable.img0130, R.drawable.img0200, 
      R.drawable.img0230, R.drawable.img0300, R.drawable.img0330, 
      R.drawable.img0354 }; 
    ImageAdapter adapter =new ImageAdapter(this, images); 
    adapter.createReflectedImages(); 
    GalleryFlow galleryFlow = (GalleryFlow) findViewById(R.id.gallery_flow); 
    galleryFlow.setAdapter(adapter); 
}

PS 1:
このように実現されたギャラリのギザギザが深刻であることが分かります。次のコードはcreateReflectImages()で使用できます。

BitmapDrawable bd = new BitmapDrawable(bitmapWithReflection);
bd.setAntiAlias(true);

その後、iv.setImageDrawable(bd)を使用する。
iv.setImageBitmapの代わりに;
基本的に鋸歯を取り除くことができます。
PS 2:
ImageAdapterが確定しているMemoryLeakの問題は、Bitmapのdecode方法でMLが発生しているようです。ImageAdapterを使うと、何度もスクリーンを回転して、OOMが現れます。現在は使用済みのbimapをrecycle()メソッドと設定nullを呼び出して直ちにsystem.gc()を呼び出すことで改善できますが、問題は明らかではありません。
精華とオススメを祝い、3つのPSを追加します。
PS 3 ON PS 1:
なぜアンチギザギザを開けたら目立たないですか?答えは、ギザギザを完全になくすことはできませんが、ギザギザを開放すると大きく改善されます。
また、なぜandroidがデフォルトでギザギザを開くのかというと、以下は私の考えです。
補間は、現在知っているアンチエイリアスのアルゴリズムであり、ピクセル間の相関を計算して、中間ピクセルを挿入して、平滑画像のエッジの効果を達成します。しかし、これは大量の演算を消費するに違いない。
テストはしていませんが、antialiasを使ったら、グラフィック性能は少なくとも30%下がると推測しています。
もちろん、ここでは複雑なグラフィックス演算には触れていませんので、ギザギザ防止機能は明らかに影響しませんが、シミュレータやローエンドモデルでテストすると問題があります。
PS 4:
tranformImageBitmap()のこの二つの言葉はどういう意味ですか?

imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));
imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));

個人的な理解は以下の通りです。
preTranslateは、画像をどのような行列変換する前にpreTranslateを行うかに相当し、postTranslateは反対に、すべての変換を行ってからpostTranlateを実行します。
変換を行う前に、画像の中心点から原点点(0,0)に全画像を移動し、変換を実行した後に原点から前の中心点に画像を移動させるという意味です。
この二つの文を加えないと、いずれの変換も画像の原点を変換の中心点とし、加えた後、どの変換も画像の中心点を変換の中心点とします。
例えば、画像を回転させるには、二つのパラメータが必要です。一つは回転角度、もう一つは回転中心の座標です。回転中心の座標は回転の効果に影響します。これは分かりますか?棒を持って、棒の端を持って回転するのと棒の真ん中を持って回るのとは違っています。preTranslateとpostTranslateが実行されても画像自体に影響はありません。影響は画像を変換する際の回転軸です。
これだけ言ったらちょっと回りくどいですが、行列変換の知識です。
PS 5 ON PS 2:
この問題はgoogle groupで十分に議論されたことがあります。一般的にはdebugモードでしか存在しないようです。今はこのコードを使っていますが、OOMの問題はありません。
ここで述べたように、皆さんのAndroidプログラムの設計に役に立ちます。