Android 自定义控件-SnakeLayout (仿gallery)


简要介绍:相信大部分用过Android Gallery控件的人,对gallery这个控件可谓是又爱又恨,gallery动画效果不错,非常实用,可是却有很多限制,从布局上来讲,gallery仅能水平放置,若想使用垂直放置的gallery,除非重写gallery。本文所述SnakeLayout继承于FrameLayout,用户可在SnakeLayout里自定义多个ImageView (大于等于3)的位置,并将指定的ID分配给所定义的ImageView;之后在主文件里进行简单的初始化后,就可以像gallery一样拖动所定义的ImageView,如同一条蛇一样连续的移动,不仅能横着拖,竖着拖,还能斜着拖,甚至绕圈圈。

效果图如下(android虚拟机长宽为800*600):

Android 自定义控件-SnakeLayout (仿gallery)
 

Android 自定义控件-SnakeLayout (仿gallery)

 
(测试版)代码如下:
package com.Snake;
/*
 * Author: Wallace Wang
 * Email: wallage@qq.com
 */
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.OnGestureListener;
import android.widget.FrameLayout;
import android.widget.ImageView;
public class SnakeLayout extends FrameLayout {
 private static final String LOG_TAG = "SnakeLayout";
 private GestureDetector mGestureDetector;
 private SnakeOnGestureListener mGestureListener;
 private List<View> ViewHolder;
 private int selectImg;
 private int totalViewNum;
 private View mContentView;
 private SnakeView ScrollView;
 private Context mContext;
 private enum State {
  ABOUT_TO_ANIMATE,
  ANIMATING,
  ANIMATE_END,
  READY,
  TRACKING
 };
 private State mState;
 private double aniStartPos;// Value = scrollNum + percent*direction;
 private double aniStopPos;// Value = scrollNum + percent*direction;
 private Date aniStartTime;
 private long aniTime = 1000;
 private double aniSpeed = 500;
 private double aniDefG = 5;
 
 private int mContentWidth = 0;
 private int mContentHeight = 0;
 private int clickItem = -1;
 private int direction = 0;
 private int movDirection = 0;
 private double percent = 0;
 private int scrollNum = 0;
 private PathScale myPathViews;
 private List<Bitmap> BmpRecViews;
 private OnSelectListener selectListener;
 private OnClickListener clickListener;
 private int currentIndex = 0;
 public SnakeLayout(Context context, AttributeSet attrs) {
  super(context, attrs);
  Log.d(LOG_TAG, "Init Snake Layout");
  mContext = context;
  TypedArray a = context.obtainStyledAttributes(attrs,
    R.styleable.SnakeLayout);
  selectImg = a.getInteger(R.styleable.SnakeLayout_selectImg, -1);
  a.recycle();
  mGestureListener = new SnakeOnGestureListener();
  mGestureDetector = new GestureDetector(mGestureListener);
  mGestureDetector.setIsLongpressEnabled(false);
  BmpRecViews = new ArrayList<Bitmap>();
  myPathViews = new PathScale();
  mState = State.READY;
 }
 public void Init(){
  for (int i = 0; i < totalViewNum; i++) {
   ImageView v = (ImageView)ViewHolder.get(i);
   v.setScaleType(ImageView.ScaleType.FIT_XY);
   v.setImageBitmap(BmpRecViews.get((i + currentIndex)% BmpRecViews.size()));
  }
 }
 
 public void addBitmap(Bitmap b){
  if(b != null)
   BmpRecViews.add(b);
 }
 
 public void addBitmap(Bitmap b, int position){
  if(b != null)
   BmpRecViews.add(position, b);
 }
 
 public void addRec(int rec){
  Bitmap b = BitmapFactory.decodeResource(this.getResources(),rec);
  if(b != null)
   BmpRecViews.add(b);
 }
 ......
代码太长,省略
 
 
res/values/attrs.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="SnakeLayout">
        <!-- Defines the special selected position Image -->
        <attr name="selectImg" format="integer" />
    </declare-styleable>
</resources>

res/values/ids.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <item type="id" name="snakeImg0" />
  <item type="id" name="snakeImg1" />
  <item type="id" name="snakeImg2" />
  <item type="id" name="snakeImg3" />
  <item type="id" name="snakeImg4" />
  <item type="id" name="snakeImg5" />
  <item type="id" name="snakeImg6" />
  <item type="id" name="snakeImg7" />
  <item type="id" name="snakeImg8" />
  <item type="id" name="snakeImg9" />
  <item type="id" name="snakeImg10" />
  <item type="id" name="snakeImg11" />
  <item type="id" name="snakeImg12" />
  <item type="id" name="snakeImg13" />
  <item type="id" name="snakeImg14" />
  <item type="id" name="snakeImg15" />
  <item type="id" name="snakeImg16" />
  <item type="id" name="snakeImg17" />
  <item type="id" name="snakeImg18" />
  <item type="id" name="snakeImg19" />
  <item type="id" name="snakeImg20" />
  <item type="id" name="snakeImg21" />
  <item type="id" name="snakeImg22" />
  <item type="id" name="snakeImg23" />
  <item type="id" name="snakeImg24" />
  <item type="id" name="snakeImg25" />
  <item type="id" name="snakeImg26" />
  <item type="id" name="snakeImg27" />
  <item type="id" name="snakeImg28" />
  <item type="id" name="snakeImg29" />
  <item type="id" name="snakeContent" />
</resources>
 
主布局文件:main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:snake="http://schemas.android.com/apk/res/com.Snake"
    android:orientation="vertical" android:background="@drawable/bj"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <com.Snake.SnakeLayout android:layout_width="fill_parent" android:layout_weight="1"
     android:id="@+id/my_snake" android:layout_height="fill_parent"
     snake:selectImg="7">
     <LinearLayout android:layout_width="fill_parent" android:orientation="vertical"
      android:layout_height="wrap_content" android:id="@id/snakeContent">
      <LinearLayout android:layout_width="fill_parent" android:paddingTop="70dip"
       android:layout_height="wrap_content">
       <TextView android:layout_width="270dip"  android:layout_height="1dip"/>
       <ImageView android:layout_width="40dip" android:id="@id/snakeImg0"
       android:layout_height="40dip"/>
      </LinearLayout>
      <LinearLayout android:layout_width="fill_parent"
       android:layout_height="wrap_content">
       <TextView android:layout_width="300dip"  android:layout_height="1dip"/>
       <ImageView android:layout_width="60dip" android:id="@id/snakeImg1"
       android:layout_height="60dip"/>
      </LinearLayout>
      <LinearLayout android:layout_width="fill_parent"
       android:layout_height="wrap_content">
       <TextView android:layout_width="295dip"  android:layout_height="1dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg2"
       android:layout_height="50dip"/>
      </LinearLayout>
      <LinearLayout android:layout_width="fill_parent"
       android:layout_height="wrap_content">
       <TextView android:layout_width="290dip"  android:layout_height="1dip"/>
       <ImageView android:layout_width="35dip" android:id="@id/snakeImg3"
       android:layout_height="35dip"/>
      </LinearLayout>
      <LinearLayout android:layout_width="fill_parent"
       android:layout_height="wrap_content">
       <TextView android:layout_width="290dip"  android:layout_height="1dip"/>
       <ImageView android:layout_width="35dip" android:id="@id/snakeImg4"
       android:layout_height="35dip"/>
      </LinearLayout>
      <LinearLayout android:layout_width="fill_parent"
       android:layout_height="wrap_content">
       <TextView android:layout_width="292dip"  android:layout_height="1dip"/>
       <ImageView android:layout_width="40dip" android:id="@id/snakeImg5"
       android:layout_height="40dip"/>
      </LinearLayout>
      <LinearLayout android:layout_width="fill_parent"
       android:layout_height="wrap_content">
       <TextView android:layout_width="320dip"  android:layout_height="1dip"/>
       <ImageView android:layout_width="40dip" android:id="@id/snakeImg6"
       android:layout_height="40dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg7"
       android:layout_height="55dip" android:paddingTop="15dip"
       android:paddingLeft="10dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg8"
       android:layout_height="55dip" android:paddingTop="15dip"
       android:paddingLeft="10dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg9"
       android:layout_height="55dip" android:paddingTop="15dip"
       android:paddingLeft="10dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg10"
       android:layout_height="50dip" android:paddingTop="10dip"
       android:paddingLeft="10dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg11"
       android:layout_height="40dip" android:paddingLeft="10dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg12"
       android:layout_height="40dip" android:paddingLeft="10dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg13"
       android:layout_height="50dip" android:paddingLeft="10dip"
       android:paddingTop="10dip"/>
      </LinearLayout>
      <LinearLayout android:layout_width="fill_parent"
       android:layout_height="wrap_content">
       <TextView android:layout_width="260dip"  android:layout_height="1dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg21"
       android:layout_height="25dip"
       android:paddingLeft="25dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg20"
       android:layout_height="55dip" android:paddingTop="20dip"
       android:paddingLeft="15dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg19"
       android:layout_height="65dip" android:paddingTop="25dip"
       android:paddingLeft="10dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg18"
       android:layout_height="65dip" android:paddingTop="25dip"
       android:paddingLeft="10dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg17"
       android:layout_height="55dip" android:paddingTop="15dip"
       android:paddingLeft="10dip"/>
       <ImageView android:layout_width="50dip" android:id="@id/snakeImg16"
       android:layout_height="45dip" android:paddingTop="5dip"
       android:paddingLeft="10dip"/>
       <ImageView android:layout_width="45dip" android:id="@id/snakeImg15"
       android:layout_height="35dip" android:paddingLeft="10dip"/>
       <ImageView android:layout_width="45dip" android:id="@id/snakeImg14"
       android:layout_height="35dip" android:paddingLeft="10dip"/>
      </LinearLayout>
     </LinearLayout>
    </com.Snake.SnakeLayout>
</LinearLayout>

Snake.java 文件:
package com.Snake;
import android.app.Activity;
import android.os.Bundle;
public class Snake extends Activity {
    /** Called when the activity is first created. */
 SnakeLayout mSnake;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        mSnake = (SnakeLayout)findViewById(R.id.my_snake);
        mSnake.addRec(R.drawable.png1);
        mSnake.addRec(R.drawable.png2);
        mSnake.addRec(R.drawable.png3);
        mSnake.addRec(R.drawable.png4);
        mSnake.addRec(R.drawable.png5);
        mSnake.addRec(R.drawable.png6);
        mSnake.addRec(R.drawable.png7);
        mSnake.addRec(R.drawable.png8);
        mSnake.addRec(R.drawable.png9);
        mSnake.addRec(R.drawable.png10);
        mSnake.addRec(R.drawable.png11);
        mSnake.addRec(R.drawable.png12);
        mSnake.addRec(R.drawable.png13);
        mSnake.addRec(R.drawable.png14);
        mSnake.addRec(R.drawable.png15);
        mSnake.addRec(R.drawable.png16);
        mSnake.addRec(R.drawable.png17);
        mSnake.addRec(R.drawable.png18);
        mSnake.addRec(R.drawable.png19);
        mSnake.addRec(R.drawable.png20);
        mSnake.addRec(R.drawable.png21);
        mSnake.addRec(R.drawable.png22);
        mSnake.addRec(R.drawable.png23);
        mSnake.addRec(R.drawable.png24);
        mSnake.addRec(R.drawable.png25);
        mSnake.addRec(R.drawable.png26);
        mSnake.addRec(R.drawable.png27);
        mSnake.addRec(R.drawable.png28);
        mSnake.Init();
    }
}

相关内容