其实就是把文章《一种利用ImageView做轮播的尝试Demo》中的代码加上可以添加照片列表实现了图片轮播,而且只要轮播速度不是很快,我这个控件可以确保不会有图片残留出来的内存泄漏,都是加载一张过完之后再释放一张。做这个系列的目的是想试试在不看别人的代码的情况下自己空闲下实现的控件可以到什么程度。
GalleryView的最新代码如下:
package com.project.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* Created by cjz on 2019/4/3.
* 图片轮播View
*/
public class GalleryView extends FrameLayout {
/**未来扩充为可以添加图片做轮播预留的数组表**/
private List<String> photoList = new ArrayList<>();
/**两个ImageView做轮播**/
private ImageView imageViewA, imageViewB;
/**宏:触发滑动**/
private final int IMAGEVIEW_SWEEP = 0;
/**是否保持轮播**/
private boolean run = true;
/**轮播线程**/
private Loop loop = null;
/**轮播线程**/
private class Loop extends Thread {
@Override
public void run() {
super.run();
while(run){
try {
Thread.sleep(80);
handler.sendEmptyMessage(IMAGEVIEW_SWEEP);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
loop = null;
}
}
/**间接操作UI,匿名内部类覆盖重写一下就够用**/
private class UIHandler extends Handler {
private ImageView currentImageView = null;
/**每次步进的像素量**/
private int step = 10;
/**选择了哪张图作为当前轮播图**/
public int picIndex = 0;
/**当前使用的位图**/
public Bitmap currentBitmapYoung = null,
currentBitmapOld = null;
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case IMAGEVIEW_SWEEP:
if(photoList == null || photoList.isEmpty()){
return;
}
if(currentImageView == null){
currentImageView = imageViewA;
}
/*A、B首尾相连,首尾在到达左边框后互为交换*/
if(currentImageView != null){
if(currentImageView.getX() + currentImageView.getWidth() <= 0){
// Log.i("picIndex", picIndex + "");
//移到屏幕外部ImageView的位图清理掉
if(currentBitmapOld != null){
currentBitmapOld.recycle();
}
//正在显示的图设为旧图
currentBitmapOld = currentBitmapYoung;
//加载新的图
currentBitmapYoung = BitmapFactory.decodeFile(photoList.get(picIndex));
if(currentImageView == imageViewA){
currentImageView = imageViewB;
currentImageView.setX(0);
//被移除左边框的ImageView移动到右侧,和左侧还没出去ImageView尾部相接
imageViewA.setX(currentImageView.getX() + currentImageView.getWidth());
// Log.i("使用","使用ImageViewB");
//右侧即将进入的ImageView加载最新的图
imageViewA.setImageBitmap(currentBitmapYoung);
} else {
currentImageView = imageViewA;
currentImageView.setX(0);
//被移除左边框的ImageView移动到右侧,和左侧还没出去ImageView尾部相接
imageViewB.setX(currentImageView.getX() + currentImageView.getWidth());
// Log.i("使用","使用ImageViewA");
//右侧即将进入的ImageView加载最新的图
imageViewB.setImageBitmap(currentBitmapYoung);
}
//图片轮选
picIndex ++;
if(picIndex >= photoList.size()){
picIndex = 0;
}
}
imageViewA.setX(imageViewA.getX() - step);
imageViewB.setX(imageViewB.getX() - step);
}
break;
}
}
}
private UIHandler handler = new UIHandler();
public GalleryView(Context context) {
super(context);
init(context);
}
public GalleryView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public GalleryView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//需要等View的大小都在onMeasure中确定完毕,才能getWidth等,不然在构造函数那里就调用getWidth根本测不出宽度等数据
//两个ImageView首尾相接
imageViewA.setX(0);
imageViewB.setX(imageViewA.getX() + imageViewA.getWidth());
startLoop();
//检测本控件的依附情况
this.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
//重新被依附就启动轮播线程
@Override
public void onViewAttachedToWindow(View v) {
run = true;
startLoop();
}
//脱离依附则关闭循环线程
@Override
public void onViewDetachedFromWindow(View v) {
run = false;
if(loop != null && loop.isAlive()){
try {
loop.interrupt();
} catch (Exception e){
e.printStackTrace();
}
}
}
});
}
/**轮播触发**/
private void startLoop(){
//初始化第一批图片
if(photoList.size() == 1){
handler.picIndex = 0;
handler.currentBitmapYoung = BitmapFactory.decodeFile(photoList.get(0));
handler.currentBitmapOld = BitmapFactory.decodeFile(photoList.get(0));
imageViewA.setImageBitmap(handler.currentBitmapOld);
imageViewB.setImageBitmap(handler.currentBitmapYoung);
} else if(photoList.size() >= 2){
handler.picIndex = 0;
handler.currentBitmapYoung = BitmapFactory.decodeFile(photoList.get(1));
handler.currentBitmapOld = BitmapFactory.decodeFile(photoList.get(0));
imageViewA.setImageBitmap(handler.currentBitmapOld);
imageViewB.setImageBitmap(handler.currentBitmapYoung);
}
//初始化循环
if(loop == null){
loop = new Loop();
}
if(!loop.isAlive()) {
loop.start();
}
}
/**初始化,加载图像等等**/
private void init(Context context){
imageViewA = new ImageView(context);
imageViewB = new ImageView(context);
imageViewA.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
imageViewB.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
//imageViewA.setImageResource(R.mipmap.ic_launcher);
//imageViewB.setImageResource(R.mipmap.ic_launcher_round);
this.addView(imageViewA);
this.addView(imageViewB);
}
public void addPhoto(String filePath){
if(filePath != null){
photoList.add(filePath);
}
}
public void addPhoto(List<String> filePaths){
if(filePaths != null) {
photoList.addAll(filePaths);
}
}
public void addPhoto(File[] files){
if(files != null){
for(File item : files){
photoList.add(item.getAbsolutePath());
// Log.i("addPhoto", item.getAbsolutePath());
}
}
}
}
使用方法:
1、在Layout文件中嵌入:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fl_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cjz.project.viewgallery.MainActivity">
<cjz.project.viewgallery.GalleryView
android:id="@+id/gv"
android:layout_width="match_parent"
android:layout_height="300dp">
</cjz.project.viewgallery.GalleryView>
</FrameLayout>
2、给对象导入要轮播的图片路径地址:
package cjz.project.viewgallery;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import java.io.File;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GalleryView gv = findViewById(R.id.gv);
File file = new File(Environment.getExternalStorageDirectory() + "/Movies/ADPagePhoto");
File[] files = file.listFiles();
gv.addPhoto(files);
}
}
我弄在了一个广告机页面上进行测试,上面图片缓慢轮播,下面播视频,效果不错而且占用内存量很低(因为没做任何缓存,而且立即释放,本就打算用在内存很小很小的安卓板上,代码效率反而不要紧,内存要精打细算)。
效果如下,图片严重压缩:
PS:其实同样的思路,也可以加上触摸事件,做个自己的ViewPager来玩,例如做个轮播切页机制,快要加载新页面的时候,回调用户的接口实现对象,让用户自己填空输入要换页时显示的对应View。