Android超简单的3D旋转效果--图片轮播式

Android超简单的3D旋转效果--图片轮播式

这段时间项目中要实现一个3D旋转的效果图,并要下载zip图包下载进行解压至手机本地,然后再显示出3D效果来,如图:


gif5新文件 (2).gif

a.首先是判断手机SD卡中是否有下载解压好的图片文件夹,当然文件夹名字可以是服务器返回过来命名的,也可以是自己写死,这个自己去设定吧,只要能判断文件夹是否存在就行,不存在就去下载资源包,存在的话就去手机中SD卡搜索图片。下载的话我用的是retrofit2,大牛已经封装好了的(原谅我懒 哈哈),看代码


QQ截图20180522144843.png

QQ截图20180522144653.png

b.下载完了就解压到手机里面


QQ截图20180522145233.png

这是解压的代码,由于解压也是异步进行的,所以当解压完成后可发送广播通知至activity中更新显示图片

/**
 * Created by Peter on 2018/5/9.
 * 解压zip文件
 */

public class ZipExtractorTask extends AsyncTask<Void, Integer, Long> {
    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<ZipEntry> entries;
        ZipFile zip = null;
        try {
            zip = new ZipFile(mInput);
            long uncompressedSize = getOriginalSize(zip);
            publishProgress(0, (int) uncompressedSize);

            entries = (Enumeration<ZipEntry>) 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<ZipEntry> entries = (Enumeration<ZipEntry>) 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<String> 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 今天是我第一次看现场版足球赛,轩辰亦是。四川九牛对战上海申梵,作为四川人的我,肯定希望九牛战胜。果然,九牛5:...
    美美大胆往前走阅读 155评论 0 0
  • 刚刚回学校几天,就想念家了!想得不是别人,是我的16年步入78岁高龄的奶奶! 由于家庭的特殊原因,从小被奶奶带大,...
    燕子想说日语阅读 192评论 0 0
  • 写作,真的不能偷懒,不能有丝毫懈怠,不能错过最佳时机。 你是否已在职场拼搏?你是否也因工作的不顺怀疑自己? 青年,...
    Catherine_龙猫阅读 240评论 0 0