【Android開發】找樂,一個笑話App的製作過程記錄

來源:互聯網
上載者:User

標籤:服務   sync   ast   使用者   jpeg   拷貝   錯誤   地方   線性   

緣起

想做一個笑話App的原因是由於在知乎上看過一個文章。做Android能夠有哪些資料能夠練手,裡面推薦了幾個資料開放平台。

在這些平台中無一不是有公用的笑話介面,當時心想這個能夠拿來練手啊,還挺有意思的,預計還能積累一點使用者。

碰巧(真的好巧)在Github中遇到了一個MVP設計模式的架構Beam,作者Jude95有一個笑話倉庫————Joy(豆逼)。就是一個做笑話的!

更巧的是用到的介面也是我在關注的介面。心想不如改造一下吧,做個升級版。自己也能夠在這個中學到別人是怎麼寫App的。

後來發現這是一個非常正確的決定。

雛形

由於是基於別人的改進。所以在寫之前就已經有雛形。當然這個雛形不是非常完好,這恰恰給了我改動的空間。在獲得作者的改動允許後,我就進一步研究這個利用MVP架構書寫的App。未改動之前:

raw=true" title="">

首先。豆逼僅僅能查看段子和查看圖片,我覺得主要的複製文本和查看大圖以及下載圖片。這些都沒有。

作者僅僅是用這個倉庫來說明MVP模式的。所以僅僅做了最主要的功能。作者也說。笑話連個id都沒有。點贊、評論什麼的根本沒法做。

那好,我就把我覺得的文本複製和圖片相關的做一下吧。

研究

MVP模式在這個項目之前我研究非常少。僅僅是聽說,可是這個項目全然給我耳目一新的感覺。MVP對Android來說實在是太實用了!

關於MVP我以後想細緻寫個文章研究一下。這裡僅僅想說明MVP使Android項目層次分明,代碼結構簡單。複用性高。參考作者的Beam。

這個項目用了非常棒的一個開原始檔控制。也是項目作者自己的控制項EasyRecyclerView,這個控制項對我來說相見恨晚。線性布局仿EasyRecyclerView已經實現了下拉重新整理,上拉載入很多其它,錯誤提示等,簡直把項目開發中可能遇到的坑都給做好了。我之前僅僅能一個一個的去實現這些功能!為什麼沒有早早的用上這個控制項。

其它的沒有重大的驚喜。可是項目整體感覺代碼量非常少,非常精簡。假設是我完畢同樣的功能的App。可能須要3倍的代碼才幹實現。

改進查看大圖

首先實現點擊查看大圖的功能。

PhotoView這個控制項也是之前不久在Github中遇到的,使用的時候沒想到居然這麼easy!僅僅須要在xml中聲明一個PhotoView。主要的放大、縮小、手勢識別都有了!太方便。可能也是北郵人論壇官方client採用的一個查看大圖的工具。

在java檔案載入圖片時則與ImageView全然同樣,這個不在贅述。

另一個拓展的地方是,單擊圖片返回(= = 一般都有吧?)。這個須要依據PhotoView的官方說明,使用Attacher來管理點擊事件,經過我測試,貌似直接聲明ImageView的點擊是不會有效果的。

圖片下載

這個App採用的是Glide載入網狀圖片。而Glide並沒有直接的下載儲存的方法,僅僅有自己拓展,耽誤了些功夫。

直接分享一段圖片下載和通知圖庫的代碼吧。

    public void saveImage(String imageUrl) {        String[] names = new String[0];        if (imageUrl != null) {            names = imageUrl.split("/");        }        String imageName = names[names.length - 1];        Glide                .with(getView())                .load(imageUrl)                .asBitmap()                .toBytes(Bitmap.CompressFormat.JPEG, 100)                .into(new SimpleTarget<byte[]>() {                    @Override                    public void onResourceReady(final byte[] resource, GlideAnimation<? super byte[]> glideAnimation) {                        new AsyncTask<Void, Void, Void>() {                            @Override                            protected Void doInBackground(Void... params) {                                if (ImageStorage.checkifImageExists(imageName)) {                                    Snackbar.make(getView().fab, "圖片已存在", Snackbar                                            .LENGTH_LONG)                                            .setAction("Action", null).show();                                    return null;                                }                                String path = Environment.getExternalStorageDirectory().toString();                                JUtils.Log("path", path);                                Bitmap bitmap = BitmapFactory.decodeByteArray(resource, 0, resource.length);                                JUtils.Log("imageName", imageName);                                ImageStorage.saveToSdCard(getView(), bitmap, imageName);                                Snackbar.make(getView().fab, "圖片已下載", Snackbar.LENGTH_LONG)                                        .setAction("Action", null).show();                                return null;                            }                        }.execute();                    }                });    }

當中ImageStorage.java:

    public class ImageStorage {        public static String saveToSdCard(Context context, Bitmap bitmap, String filename) {            String stored = null;            File sdcard = Environment.getExternalStorageDirectory();            File folder = new File(sdcard.getAbsoluteFile(), "FindJoy");//the dot makes this directory hidden to            // the            // user            folder.mkdir();            File file = new File(folder.getAbsoluteFile(), filename + ".jpg");            if (file.exists())                return stored;            try {                FileOutputStream out = new FileOutputStream(file);                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);                out.flush();                out.close();                stored = "success";                JUtils.Log("stored", stored);            } catch (Exception e) {                e.printStackTrace();            }            // 其次把檔案插入到系統圖庫            try {                MediaStore.Images.Media.insertImage(context.getContentResolver(),                        file.getAbsolutePath(), filename, null);            } catch (FileNotFoundException e) {                e.printStackTrace();            }            // 最後通知圖庫更新            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + file                    .getAbsolutePath())));            return stored;        }        public static File getImage(String imagename) {            File mediaImage = null;            try {                String root = Environment.getExternalStorageDirectory().toString();                File myDir = new File(root);                if (!myDir.exists())                    return null;                mediaImage = new File(myDir.getPath() + "/FindJoy/" + imagename);            } catch (Exception e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            return mediaImage;        }        public static boolean checkifImageExists(String imagename) {            Bitmap b = null;            File file = ImageStorage.getImage("/" +                    imagename + "" +                    ".jpg");            String path = file.getAbsolutePath();            if (path != null)                b = BitmapFactory.decodeFile(path);            if (b == null || b.equals("")) {                return false;            }            return true;        }    }

為什麼之前我試了非常久可是一直發現圖庫沒有圖片呢?一直以為是自己的圖片沒有儲存下來,後來用圖庫的查看檔案夾的方式發現了FindJoy檔案夾。

原來是須要通知圖庫更新,否則圖片不會再圖庫中顯示。詳細請看上面代碼。

複製段子

這個本身是不麻煩的,出現故障的地方在於,這個MVP架構中怎麼對這個List加上OnItemClilkListner。

本身我就不非常熟。這個地方犯了不少錯誤,我怎麼沒想到看EasyRecyclerView的官方說明呢?

解決方案是在TextViewHolder中的itemView加上:

    itemView.setOnClickListener(view ->            new MaterialDialog.Builder(getContext())                    .title(R.string.select)                    .content(R.string.copy)                    .positiveText(R.string.agree)                    .negativeText(R.string.disagree)                    .onPositive((dialog, which) -> {                        // Gets a handle to the clipboard service.                        ClipboardManager clipboard = (ClipboardManager) getContext().                                getSystemService(Context.CLIPBOARD_SERVICE);                        // Creates a new text clip to put on the clipboard                        ClipData clip = ClipData.newPlainText("joy", data.getText());                        // Set the clipboard‘s primary clip.                        clipboard.setPrimaryClip(clip);                        Snackbar.make(itemView, "已將該段子拷貝到粘貼板", Snackbar.LENGTH_SHORT).show();                    })                    .show()    );

官方庫還有能夠設定EasyRecyclerView的監聽的方法,效果是一樣的。

友盟統計

友盟統計可能是我自己往外發包的一個必選的項了,由於要知道App的使用方式啊。

這次發現友盟統計比曾經好用多了。jar包也放到了jCenter()倉庫,非常方便了。

這裡要贊一下這個MVP庫的優點了。居然能夠讓全部的Activity的生命週期都調用同一段代碼來實現友盟統計中要求的全部Actvity的OnResume()和OnPause()方法中都調用統計方法。

實現是通過一個頂級管理類MyActivityLifeCycleDelegate繼承ActivityLifeCycleDelegate,在裡面設定友盟統計的方法。

    public class MyActivityLifeCycleDelegate extends ActivityLifeCycleDelegate {        public MyActivityLifeCycleDelegate(Activity act) {            super(act);        }        @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            JUtils.Log("onCreate" + getActivity().getClass().getName());        }        @Override        protected void onPause() {            super.onPause();            JUtils.Log("onPause");            MobclickAgent.onPause(getActivity());        }        @Override        protected void onResume() {            super.onResume();            JUtils.Log("onResume");            MobclickAgent.onResume(getActivity());        }    }

然後在App的Application中

Beam.setActivityLifeCycleDelegateProvider(MyActivityLifeCycleDelegate::new);

上面這行代碼是IDE自己簡化的。好高端啊,居然有點不明確是怎麼回事了。。)

哦,對了,不要忘記在Manifest中聲明友盟的appkey。

嗯。統計就整合好了。

自己主動更新

同樣是友盟的服務。我也以為僅僅是幾分鐘的事情就搞定了,可是由於自己的問題,耽誤了一段時間,居然還想著把這個鍋扔給友盟。

好吧,我錯了。

這個和統計不一樣的是須要手動下載包放到項目當中,當中包括了一個.so檔案。

由於在app的gradle中聲明了這句:

    compile fileTree(include: [‘*.jar‘], dir: ‘libs‘)

我就以為萬事大吉了。其實我開啟了友盟的debug模式才看了出來是我的.so沒有載入進去。

嗯。jni應該這麼聲明。我給忘了:

    sourceSets {        main {            jniLibs.srcDirs = [‘libs‘]        }    }

這樣.so檔案就能載入進去了。

而友盟自己主動更新僅僅須要在MainActivity中寫一句代碼:

    UmengUpdateAgent.update(this);

非常酷對不正確?

自己主動更新是依據app versionCode來推斷的。更新的時候注意改動。

App

這些功能做完之後我改動了一下配色。終於效果大體,部分功能未。

raw=true" title="">

應用市場

嗯,這些都實現了之後就上線市集了,主要有這幾個:

  • 小米市集:http://app.mi.com/detail/286105
  • 應用寶:http://android.myapp.com/myapp/detail.htm?

    apkName=com.fuxuemingzhu.findjoy

  • 豌豆莢:http://www.wandoujia.com/apps/com.fuxuemingzhu.findjoy
  • Fir.im:http://fir.im/axy4

能夠掃碼下載:

應用寶下載:

raw=true" title="">

豌豆莢下載:

Fir.im下載:

盡量不要用Fir。由於Fir沒有直觀的下載數目統計,嗯。盡量通過正規市集吧。

下載這事還得大家捧個場。

結語

儘管是一個非常easy的App,可是卻包括著非常多的心思在裡面。並且嘗試新的東西的時候能夠學到不少東西。這個是值得肯定的。畢竟我如今有種想把之前的App都揉碎又一次來寫的衝動。畢竟抵擋不住 MVP + Material Design的雙重誘惑啊!

Android 開發還有非常長的路要走。

本項目已經全然開源,代碼在:https://github.com/fuxuemingzhu/FindJoy,歡迎Star和Fork.

【Android開發】找樂,一個笑話App的製作過程記錄

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.