前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(一百二十四)自定义相册

Android开发笔记(一百二十四)自定义相册

作者头像
aqi00
发布2019-01-18 15:03:48
2K0
发布2019-01-18 15:03:48
举报
文章被收录于专栏:老欧说安卓

画廊Gallery

Gallery是一个早期的画廊控件,左右滑动手势可展示内嵌的图片列表,类似于一个平面的万花筒。虽然Android现在将Gallery标记为Deprecation(表示已废弃),建议开发者采用HorizontalScrollView或者ViewPager来代替,但是Gallery用做自定义相册来轮播图片其实是个挺好的选择,所以下面我们还是简单介绍它的用法,并结合其它控件加深对图像开发的理解。 Gallery的常用属性说明如下: spacing : 指定图片之间的间隔大小。 unselectedAlpha : 指定未选定图片的透明度。取值为0到1,0表示完全透明,1表示完全不透明。 Gallery的常用方法说明如下: setSpacing : 设置图片之间的间隔大小。 setUnselectedAlpha : 设置未选定图片的透明度。 setAdapter : 设置图像视图的适配器。 getSelectedItemId : 获取当前选中的图像id。0表示第一个图像。 setSelection : 设置当前选中第几个图像。 setOnItemClickListener : 设置单项的点击监听器。 现在我们结合Gallery与ImageView来观看画廊的相册效果,首先放置一个FrameLayout布局,里面放入一个Gallery控件与一个ImageView控件,其中ImageView控件要充满整个屏幕,Gallery控件可放在屏幕上方或下方;然后监听Gallery控件的单项点击事件,点击指定图片项时,便给ImageView控件填充该图片,也就是点小图看大图。 下面是Gallery与ImageView结合使用的效果截图:

下面是Gallery与ImageView结合使用的代码例子:

代码语言:javascript
复制
import com.example.exmcard.adapter.GalleryAdapter;
import com.example.exmcard.util.MetricsUtil;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Gallery;
import android.widget.ImageView;

@SuppressWarnings("deprecation")
public class GalleryActivity extends Activity implements OnItemClickListener {

	private ImageView iv_gallery;
	private Gallery gl_gallery;

	// 图片数组
	private int[] mImageRes = {
			R.drawable.scene1, R.drawable.scene2,
			R.drawable.scene3, R.drawable.scene4,
			R.drawable.scene5, R.drawable.scene6
			};
	private int dip_pad;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_gallery);
		dip_pad = MetricsUtil.dip2px(this, 20);

		iv_gallery = (ImageView) findViewById(R.id.iv_gallery);
		iv_gallery.setImageResource(mImageRes[0]);

		gl_gallery = (Gallery) findViewById(R.id.gl_gallery);
		gl_gallery.setPadding(dip_pad, dip_pad, dip_pad, dip_pad);
		gl_gallery.setSpacing(dip_pad);
		gl_gallery.setUnselectedAlpha(0.5f);
		gl_gallery.setAdapter(new GalleryAdapter(this, mImageRes));
		gl_gallery.setOnItemClickListener(this);
	}

	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
		iv_gallery.setImageResource(mImageRes[position]);
	}

}

图像切换ImageSwitcher

可能大家已经发现,前面Gallery与ImageView在切换大图时比较生硬,前后两张图片闪一下就切过去,用户体验不够友好。那有没有办法让图片切换自然些呢,比如说通过渐变动画的方式?答案肯定是有的,就是把ImageView换成ImageSwitcher,通过ImageSwitcher控件来实现图片的切换动画。ImageSwitcher实质是个视图动画师ViewAnimator,用于处理前后图像的变换动画;与之对应的是,TextSwitcher用于处理前后文本的变换动画;另外ViewFlipper也是从ViewAnimator派生而来,则是用于处理两个页面视图的变换动画。有关ViewFlipper的详细介绍参见《Android开发笔记(二十一)横幅轮播页》。 ImageSwitcher的常用方法说明如下: setFactory : 设置一个视图工厂。该视图工厂对象从ViewFactory派生而来,内部需要重写makeView方法来返回视图工厂里的具体视图。对于ImageSwitcher来说,视图工厂应当返回的当然是ImageView对象了。 setImageResource : 设置当前图像的资源ID。该方法与下面的setImageDrawable和setImageURI为三选一操作,调用了其中一个方法,就无需调用另外两个方法。 setImageDrawable : 设置当前图像的Drawable对象。 setImageURI : 设置当前图像的URI地址。 setInAnimation : 设置当前图像的进入动画。 setOutAnimation : 设置前一个图像的退出动画。 按照ImageSwitcher的上述方法,我们便能实现前后两个图像的切换动画(如淡入淡出动画)。可是还没有实现左右滑动切换图片的功能,既然Gallery上的小图能够左右滑动,那么我们希望ImageSwitcher的大图也能够左右滑动,这时要借助于手势事件来实现滑动切换功能。首先定义一个GestureDetector对象;然后调用ImageSwitcher的setOnTouchListener方法设置触摸监听器OnTouchListener,在该监听器的onTouch方法中让GestureDetector对象接管事件处理;最后重写GestureDetector对象的手势监听器OnGestureListener,主要是在onFling方法中增加对左滑和右滑的处理逻辑判断。 下面是Gallery与ImageSwitcher结合使用的效果截图:

下面是Gallery与ImageSwitcher结合使用的代码例子:

代码语言:javascript
复制
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.Gallery;
import android.widget.ImageSwitcher;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.ViewSwitcher.ViewFactory;

import com.example.exmcard.adapter.GalleryAdapter;
import com.example.exmcard.util.MetricsUtil;

@SuppressWarnings("deprecation")
public class SwitcherActivity extends Activity implements 
		OnTouchListener, OnItemClickListener {

	private ImageSwitcher is_switcher;
	private Gallery gl_switcher;

	// 图片数组
	private int[] mImageRes = {
			R.drawable.scene1, R.drawable.scene2,
			R.drawable.scene3, R.drawable.scene4,
			R.drawable.scene5, R.drawable.scene6
			};
	private int dip_pad;

	private GestureDetector mGesture;
	private float mFlipGap = 20f;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_switcher);
		dip_pad = MetricsUtil.dip2px(this, 20);

		is_switcher = (ImageSwitcher) findViewById(R.id.is_switcher);
		is_switcher.setFactory(new ViewFactoryImpl());
		is_switcher.setImageResource(mImageRes[0]);
		mGesture = new GestureDetector(this, new GestureListener(this));
		is_switcher.setOnTouchListener(this);
		
		gl_switcher = (Gallery) findViewById(R.id.gl_switcher);
		gl_switcher.setPadding(dip_pad, dip_pad, dip_pad, dip_pad);
		gl_switcher.setSpacing(dip_pad);
		gl_switcher.setUnselectedAlpha(0.5f);
		gl_switcher.setAdapter(new GalleryAdapter(this, mImageRes));
		gl_switcher.setOnItemClickListener(this);
	}

	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
		is_switcher.setInAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_in));
		is_switcher.setOutAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_out));
		is_switcher.setImageResource(mImageRes[position]);
	}

	public class ViewFactoryImpl implements ViewFactory {

		@Override
		public View makeView() {
			ImageView iv = new ImageView(SwitcherActivity.this);
			iv.setBackgroundColor(0xFFFFFFFF);
			iv.setScaleType(ScaleType.FIT_XY);
			iv.setLayoutParams(new ImageSwitcher.LayoutParams(
					LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
			return iv;
		}

	}

	@SuppressLint("ClickableViewAccessibility")
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		mGesture.onTouchEvent(event);
		return true;
	}

	private class GestureListener implements GestureDetector.OnGestureListener {
		private Context mContext;
		private GestureListener(Context context) {
			mContext = context;
		}

		@Override
		public final boolean onDown(MotionEvent event) {
			return true;
		}

		@Override
		public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
			if (e1.getX() - e2.getX() > mFlipGap) {
				is_switcher.setInAnimation(AnimationUtils.loadAnimation(mContext,
                        R.anim.push_left_in));
				is_switcher.setOutAnimation(AnimationUtils.loadAnimation(mContext,
                        R.anim.push_left_out));
				int next_pos = (int) (gl_switcher.getSelectedItemId()+1);
				if (next_pos >= mImageRes.length) {
					next_pos = 0;
				}
				is_switcher.setImageResource(mImageRes[next_pos]);
				gl_switcher.setSelection(next_pos);
				return true;
			}
			if (e1.getX() - e2.getX() < -mFlipGap) {
				is_switcher.setInAnimation(AnimationUtils.loadAnimation(mContext,
                        R.anim.push_right_in));
				is_switcher.setOutAnimation(AnimationUtils.loadAnimation(mContext,
                        R.anim.push_right_out));
				int pre_pos = (int) (gl_switcher.getSelectedItemId()-1);
				if (pre_pos < 0) {
					pre_pos = mImageRes.length-1;
				}
				is_switcher.setImageResource(mImageRes[pre_pos]);
				gl_switcher.setSelection(pre_pos);
				return true;
			}
			return false;
		}

		@Override
		public final void onLongPress(MotionEvent event) {
		}

		@Override
		public final boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
			return false;
		}

		@Override
		public final void onShowPress(MotionEvent event) {
		}

		@Override
		public boolean onSingleTapUp(MotionEvent event) {
			return false;
		}

	}

}

调色板Palette

Palette是Android在5.0中引入的调色板控件,它用于分析一个位图对象的整体色调,最后给出样品的色彩RGB值,这样开发者就可以根据具体图片动态设置整个页面的背景色,从而实现统一的页面风格。 Palette在android-support-v7-palette.jar中定义,同时需要最新的android-support-v4.jar支持。使用之前先在sdk的“sdk\extras\android\support\v7\palette\libs”目录中找到jar包并在自己的工程中引用,如果在运行过程中报错“Caused by: java.lang.NoClassDefFoundError: android.support.v4.graphics.ColorUtils”,则是因为Palette调用了v4包中新加的类ColorUtils,解决办法是把最新的android-support-v4.jar导入到你的工程。 Palette的常用方法主要是两个: from : 从指定的Bitmap对象生成一个调色板建造者对象Palette.Builder。然后调用该Builder对象的generate方法即开始色调分析,generate方法的参数是个PaletteAsyncListener监听器,监听器的onGenerated方法就是完成分析之后的回调处理。 getVibrantSwatch : 获得Palette对象的样品。该方法在onGenerated中调用,返回值是Palette.Swatch样品对象,调用该样品对象的getRgb方法即可获得样品的色彩值。 下面是Gallery与Palette结合使用的效果截图:

下面是Gallery与Palette结合使用的代码例子:

代码语言:javascript
复制
import com.example.exmcard.adapter.GalleryAdapter;
import com.example.exmcard.util.MetricsUtil;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.graphics.Palette;
import android.support.v7.graphics.Palette.PaletteAsyncListener;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.AdapterView.OnItemClickListener;

@SuppressWarnings("deprecation")
public class PaletteActivity extends Activity implements 
		OnItemClickListener, PaletteAsyncListener {

	private ImageView iv_palette;
	private Gallery gl_palette;

	// 图片数组
	private int[] mImageRes = {
			R.drawable.scene1, R.drawable.scene2,
			R.drawable.scene3, R.drawable.scene4,
			R.drawable.scene5, R.drawable.scene6
			};
	private int dip_pad;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_palette);
		dip_pad = MetricsUtil.dip2px(this, 20);

		iv_palette = (ImageView) findViewById(R.id.iv_palette);
		showBackground(mImageRes[0]);
		
		gl_palette = (Gallery) findViewById(R.id.gl_palette);
		gl_palette.setPadding(dip_pad, dip_pad, dip_pad, dip_pad);
		gl_palette.setSpacing(dip_pad);
		gl_palette.setUnselectedAlpha(0.5f);
		gl_palette.setAdapter(new GalleryAdapter(this, mImageRes));
		gl_palette.setOnItemClickListener(this);
	}
	
	private void showBackground(int res_id) {
		Drawable drawable = getResources().getDrawable(res_id);
		Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
		Palette.Builder builder = Palette.from(bitmap);
		builder.generate(this);
	}

	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
		showBackground(mImageRes[position]);
	}

	@Override
	public void onGenerated(Palette palette) {
		Palette.Swatch swatch = palette.getVibrantSwatch();
		if (swatch != null) {
			iv_palette.setBackgroundColor(swatch.getRgb());
		}
	}

}

卡片视图CardView

CardView也是Android在5.0中新引入的卡片视图控件,顾名思义它拥有一个卡片式的圆角边框,边框外缘有一圈阴影,边框内缘有一圈空白。该控件的实现原理并不复杂,事实上早期便有许多人自己写了类似卡片效果的控件,只不过后来Android顺应民意推出了原生的卡片视图。 使用CardView之前,要把“sdk\extras\android\support\v7\cardview”导入为一个库工程,并引用到自己的工程中。如果在app运行的时候报错:“Caused by: java.lang.NoClassDefFoundError: android.support.v7.cardview.R$styleable”,这是因为CardView源码中引用了android.support.v7.cardview.R.styleable,而开发者自己的工程包名不是android.support.v7.cardview,所以就会找不到这个R$styleable。 解决步骤如下: 1、要引用整个android-support-v7-cardview工程,不能直接把android-support-v7-cardview.jar复制到自己工程的libs目录。 2、把project.properties里面的“target=android-19”改为“target=android-21”,注意库工程和自己的工程都要改。 3、库工程和自己的工程都Clean Project,然后再编译运行。 CardView的常用属性说明如下(因为引用的是库工程,所以CardView节点的属性要像自定义控件一样对待,即先在根节点定义一个命名空间app指向res-auto,然后再使用app:属性名称来定义属性值,不可直接使用android:属性名称): cardBackgroundColor : 指定卡片的背景颜色。 cardCornerRadius : 指定卡片的圆角半径。 cardElevation : 指定卡片内容距离阴影边缘的间隔。 contentPadding : 指定卡片边缘阴影的高程,即阴影的宽度。 CardView的常用方法说明如下: setCardBackgroundColor : 设置卡片的背景颜色。 setRadius : 设置卡片的圆角半径。 setContentPadding : 设置卡片内容距离阴影边缘的间隔。 setCardElevation : 设置卡片边缘阴影的高程,即阴影的宽度。 下面是Gallery与CardView结合使用的效果截图:

下面是Gallery与CardView结合使用的代码例子:

代码语言:javascript
复制
import com.example.exmcard.util.MetricsUtil;

import android.content.Context;
import android.support.v7.widget.CardView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;

@SuppressWarnings("deprecation")
public class CardAdapter extends BaseAdapter {

	private Context mContext;
	private int[] mImageRes;
	private int dip_pad;
	private int dip_radius;

	public CardAdapter(Context context, int[] imageRes) {
		mContext = context;
		mImageRes = imageRes;
		dip_pad = MetricsUtil.dip2px(mContext, 20);
		dip_radius = MetricsUtil.dip2px(mContext, 5);
	}

	@Override
	public int getCount() {
		return mImageRes.length;
	}

	@Override
	public Object getItem(int position) {
		return mImageRes[position];
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		CardView card = new CardView(mContext);
		//这里不能使用LinearLayout.LayoutParams。否则会报错“java.lang.ClassCastException: android.widget.LinearLayout$LayoutParams cannot be cast to android.widget.Gallery$LayoutParams”
		card.setLayoutParams(new Gallery.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
		card.setRadius(dip_radius);
		card.setContentPadding(dip_pad, dip_pad, dip_pad, dip_pad);
		card.setCardElevation(3f);
		//card.setCardBackgroundColor(Color.GREEN);

		ImageView iv = new ImageView(mContext);
		iv.setImageResource(mImageRes[position]);
		iv.setLayoutParams(new Gallery.LayoutParams(120, 160));
		iv.setScaleType(ImageView.ScaleType.FIT_XY);
		card.addView(iv);

		return card;
	}

}

下面是使用CardView的布局文件:

代码语言:javascript
复制
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:padding="5dp" >

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center_horizontal|top"
        android:orientation="vertical" >

        <android.support.v7.widget.CardView
            android:id="@+id/cv_one"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal|top"
            app:cardCornerRadius="20dp" >

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:scaleType="fitCenter"
                android:src="@drawable/scene1" />
        </android.support.v7.widget.CardView>
    </LinearLayout>

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center_horizontal|top"
        android:orientation="vertical" >

        <android.support.v7.widget.CardView
            android:id="@+id/cv_two"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal|top" >

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" >

                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:scaleType="fitCenter"
                    android:src="@drawable/scene2" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:text="风景如画"
                    android:textColor="#000000"
                    android:textSize="17sp" />
            </LinearLayout>
        </android.support.v7.widget.CardView>
    </LinearLayout>

</LinearLayout>

点击下载本文用到的自定义相册的工程代码 点此查看Android开发笔记的完整目录

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016年09月06日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 画廊Gallery
  • 图像切换ImageSwitcher
  • 调色板Palette
  • 卡片视图CardView
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档