Android超简单的3D旋转效果--图片轮播式
这段时间项目中要实现一个3D旋转的效果图,并要下载zip图包下载进行解压至手机本地,然后再显示出3D效果来,如图:
a.首先是判断手机SD卡中是否有下载解压好的图片文件夹,当然文件夹名字可以是服务器返回过来命名的,也可以是自己写死,这个自己去设定吧,只要能判断文件夹是否存在就行,不存在就去下载资源包,存在的话就去手机中SD卡搜索图片。下载的话我用的是retrofit2,大牛已经封装好了的(原谅我懒 哈哈),看代码
b.下载完了就解压到手机里面
这是解压的代码,由于解压也是异步进行的,所以当解压完成后可发送广播通知至activity中更新显示图片
/**
* Created by Peter on 2018/5/9.
* 解压zip文件
*/
public class ZipExtractorTask extends AsyncTask {
private final String TAG = "ZipExtractorTask";
private final File mInput;
private final File mOutput;
// private final ProgressDialog mDialog;
private int mProgress = 0;
private final Context mContext;
private boolean mReplaceAll;
//0解压展示产品类型,1解压DIY中戒托类型(用于发送不同的广播)
private int type;
WebView mWebView;
public ZipExtractorTask(String in, String out, Context context, boolean replaceAll, int type){
super();
this.type = type;
mInput = new File(in);
mOutput = new File(out);
if(!mOutput.exists()){
if(!mOutput.mkdirs()){
Log.e(TAG, "Failed to make directories:"+mOutput.getAbsolutePath());
}
}
mContext = context;
mReplaceAll = replaceAll;
}
@Override
protected Long doInBackground(Void... params) {
// TODO Auto-generated method stub
return unzip();
}
@Override
protected void onPostExecute(Long result) {
// TODO Auto-generated method stub
//super.onPostExecute(result);
if(isCancelled())
return;
//这里表示解压完成 可以进行显示WebView 发送广播 并更新保存的 时间
//更新产品展示的广播
if (type == 0){
Intent intent1 = new Intent("com.sl.unzip");
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent1);
}
//解压DIY中戒托zip后的广播
if (type == 1){
Intent intent1 = new Intent("com.sl.unzip.jt");
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent1);
}
}
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
//super.onPreExecute();
}
@Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
}
private long unzip(){
long extractedSize = 0L;
Enumeration entries;
ZipFile zip = null;
try {
zip = new ZipFile(mInput);
long uncompressedSize = getOriginalSize(zip);
publishProgress(0, (int) uncompressedSize);
entries = (Enumeration) zip.entries();
while(entries.hasMoreElements()){
ZipEntry entry = entries.nextElement();
if(entry.isDirectory()){
continue;
}
File destination = new File(mOutput, entry.getName());
if(!destination.getParentFile().exists()){
Log.e(TAG, "make="+destination.getParentFile().getAbsolutePath());
destination.getParentFile().mkdirs();
}
if(destination.exists()&&mContext!=null&&!mReplaceAll){
}
ProgressReportingOutputStream outStream = new ProgressReportingOutputStream(destination);
extractedSize+=copy(zip.getInputStream(entry),outStream);
outStream.close();
}
} catch (ZipException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
zip.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return extractedSize;
}
private long getOriginalSize(ZipFile file){
Enumeration entries = (Enumeration) file.entries();
long originalSize = 0l;
while(entries.hasMoreElements()){
ZipEntry entry = entries.nextElement();
if(entry.getSize()>=0){
originalSize+=entry.getSize();
}
}
return originalSize;
}
private int copy(InputStream input, OutputStream output){
byte[] buffer = new byte[1024*8];
BufferedInputStream in = new BufferedInputStream(input, 1024*8);
BufferedOutputStream out = new BufferedOutputStream(output, 1024*8);
int count =0,n=0;
try {
while((n=in.read(buffer, 0, 1024*8))!=-1){
out.write(buffer, 0, n);
count+=n;
}
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return count;
}
private final class ProgressReportingOutputStream extends FileOutputStream {
public ProgressReportingOutputStream(File file)
throws FileNotFoundException {
super(file);
// TODO Auto-generated constructor stub
}
@Override
public void write(byte[] buffer, int byteOffset, int byteCount)
throws IOException {
// TODO Auto-generated method stub
super.write(buffer, byteOffset, byteCount);
mProgress += byteCount;
publishProgress(mProgress);
}
}
}
c.在activity界面初始化时候创建广播接收器
/*
接收解压完成的广播,更新图片显示
*/
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.sl.unzip");
BroadcastReceiver mItemViewListClickReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mSVProgressHUD.dismiss();
show3Dphoto();
}
};
broadcastManager.registerReceiver(mItemViewListClickReceiver, intentFilter);
d.接收到了图片解压完成的消息就显示出来吧
//获取sd卡中的3D图片
private void show3Dphoto() {
try {
//根据路径查找手机SD卡中该路径下的所有图片
List list = Utils.getImagePathFromSD(Constants.PICTHER_PRODUCT_PATH + "tupian");
if (list.size() != 0) {
//对图片路径列表进行排序,升序
Collections.sort(list, Collator.getInstance(java.util.Locale.CHINA));
//图片总数
maxNum = list.size();
//SD卡本地要展示的图片路径数组
srcs = list.toArray(new String[list.size()]);
//从本地取图片(在cdcard中获取)
bitmap = Utils.getLoacalBitmap(srcs[scrNum]);
mImg_produce.setImageBitmap(bitmap);
//开始计时自动旋转轮播图片
startTipsTimer();
} else {
//如果搜索到的图片列表大小为0则重新下载资源
onStartDownload("http://adel.ifs.waltzcn.com/upload/201805/10/V27275.zip", "tupian", RotatingActivity.this);
Log.i("TAG","未找到3D图片");
}
} catch (Exception e) {
e.printStackTrace();
if (mSVProgressHUD != null) {
if (mSVProgressHUD.isShowing()) {
mSVProgressHUD.dismiss();
}
}
Log.i("TAG","搜索图片失败");
}
}
这段代码主要就是搜索下载后的图片位置,添加并按名字排序到list中。排序主要是让播放更有顺序,因为这是轮播图片,肯定得看起来要自然,比如有80张图片,分别命名为a01、a02...a80,那么就按这个顺序播放下去。
e.自动轮播旋转图片,我这里就用Handler实现,一个Message用来通知是否自动开始轮播,一个Message用于自动轮播图片,这个要注意区分开
//handler中接受定时消息来更新展示的3D图片
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_WHIRLIGIG:
modifySrcR();
mHandler.sendEmptyMessageDelayed(MSG_WHIRLIGIG, 100);
break;
case MSG_START_WHIRLIGIG:
mHandler.sendEmptyMessageDelayed(MSG_WHIRLIGIG, 100);
break;
}
}
};
private Runnable tipsShowRunable = new Runnable() {
@Override
public void run() {
mHandler.obtainMessage(MSG_START_WHIRLIGIG).sendToTarget();
}
};
//重置计时
public void resetTipsTimer() {
if (mHandler.hasMessages(MSG_WHIRLIGIG)) {
mHandler.removeMessages(MSG_WHIRLIGIG);
}
mHandler.removeCallbacks(tipsShowRunable);
mHandler.postDelayed(tipsShowRunable, 2000);
}
/**
* <无操作时开始计时>
* <功能详细描述>
*
* @see [类、类#方法、类#成员]
*/
public void startTipsTimer() {
if (mHandler.hasMessages(MSG_WHIRLIGIG)) {
mHandler.removeMessages(MSG_WHIRLIGIG);
}
mHandler.postDelayed(tipsShowRunable, 2000);
}
/**
* <结束当前计时,重置计时>
* <功能详细描述>
*
* @see [类、类#方法、类#成员]
*/
public void endTipsTimer() {
if (mHandler.hasMessages(MSG_WHIRLIGIG)) {
mHandler.removeMessages(MSG_WHIRLIGIG);
}
mHandler.removeCallbacks(tipsShowRunable);
}
f.根据图片位置显示出来
// 向右滑动修改资源
private void modifySrcR() {
if (srcs == null) {
return;
}
if (scrNum > maxNum) {
scrNum = 1;
}
if (scrNum > 0) {
bitmap = Utils.getLoacalBitmap(srcs[scrNum - 1]); //从本地取图片(在cdcard中获取)
mImg_produce.setImageBitmap(bitmap);
scrNum++;
}
}
// 向左滑动修改资源
private void modifySrcL() {
if (srcs == null) {
return;
}
if (scrNum <= 0) {
scrNum = maxNum;
}
if (scrNum <= maxNum) {
bitmap = Utils.getLoacalBitmap(srcs[scrNum - 1]); //从本地取图片(在cdcard中获取)
mImg_produce.setImageBitmap(bitmap);
scrNum--;
}
}
g.最后把图片可触摸左右旋转的事件监听加上
//3D图布局触摸监听
mLayout_produce_show.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
resetTipsTimer();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
currentX = (int) event.getX();
// 判断手势滑动方向,并切换图片,旋转的灵敏度可在这里调
if (currentX - startX > 3) {
for (int i = 0; i < (currentX - startX) / 3; i++) {
modifySrcR();
}
} else if (currentX - startX < -3) {
for (int i = 0; i < (currentX - startX) / -3; i++) {
modifySrcL();
}
}
// 重置起始位置
startX = (int) event.getX();
break;
}
return true;
}
});
左右旋转的触摸主要是根据手指触摸的位置坐标来结算的。当手指按下去的时候,就停止自动旋转,重置计时发送Message。
Over!实现这个功能就到这里了,还有一些细节我可能也没写清楚或者写得不适当,项目源码更详细,请指教。
附上源码
https://github.com/peterMa1999/3DRotatingDome