4.01.2016

Android ImageView 設為空

原本的ImageView有塞圖,要將它清空:

        try{
            Bitmap _IMG = ((BitmapDrawable) view.getDrawable()).getBitmap();
            if(_IMG != null && !_IMG.isRecycled())
            {
                _IMG.recycle();
            }
            if(resource != -1) view.setImageResource(resource);
            else view.setImageDrawable(null);
        }catch (NullPointerException e){}

8.07.2015

Android Studio build error - Error:Android Dex: [...] Unable to execute DX

Error:Android Dex: [EdiLife] Unable to execute DX
Error:Android Dex: [EdiLife] com.android.dex.DexException: Multiple dex files define Lcom/google/android/gcm/GCMBaseIntentService;
Error:Android Dex: [EdiLife] at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:594)
Error:Android Dex: [EdiLife] at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:552)
Error:Android Dex: [EdiLife] at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:533)
Error:Android Dex: [EdiLife] at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:170)
Error:Android Dex: [EdiLife] at com.android.dx.merge.DexMerger.merge(DexMerger.java:188)
Error:Android Dex: [EdiLife] at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)
Error:Android Dex: [EdiLife] at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)
Error:Android Dex: [EdiLife] at com.android.dx.command.dexer.Main.run(Main.java:230)
Error:Android Dex: [EdiLife] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Error:Android Dex: [EdiLife] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
Error:Android Dex: [EdiLife] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
Error:Android Dex: [EdiLife] at java.lang.reflect.Method.invoke(Method.java:606)
Error:Android Dex: [EdiLife] at org.jetbrains.android.compiler.tools.AndroidDxRunner.runDex(AndroidDxRunner.java:161)
Error:Android Dex: [EdiLife] at org.jetbrains.android.compiler.tools.AndroidDxRunner.main(AndroidDxRunner.java:294)
Error:Android Dex: [EdiLife] at com.intellij.rt.execution.CommandLineWrapper.main(CommandLineWrapper.java:121)

在Android Studio環境下Compiler 專案,結果跳出這惱人的錯誤,
後來查一查,"Multiple dex files define Lcom/google/android/gcm/GCMBaseIntentService"
發現是匯入到相同的兩個lib,因此package name有衝突,將一個移除後就可以正常Compiler了。

6.10.2015

Android Studio build error - JDK CreateProcess error=2, 系統找不到指定的檔案。

Cannot run program "C:\Program Files\Java\jdk1.8.0_25/bin/java": CreateProcess error=2, 系統找不到指定的檔案。

發生這錯誤,原因是我把jdk1.8砍掉了,換成jdk1.7(至於為什麼要降版,因為要做ProGuard混淆程式碼必須在jdk1.7以下版本才有支援)

很奇怪的是系統的環境變數(PATH)我也改了也重開機了,Android Studio就是不會自動抓取參數值,所以我找了又找看是哪邊寫死了參數。

後來發現在"User/(使用者名稱)/.AndroidStudioBeta/config/options/jdk.table.xml"裡面寫死了很多jdk1.8.0_25值,ORZ...把他全改成1.7的路徑後,就都正常了。

5.05.2015

Android : ViewPager , 循環滑屏

使用ViewPager的layout,可左右一直滑動無限制的循環,畫面呈現如下




Layout的主要元件為"android.support.v4.view.ViewPager",可在SDK包裡面找到這份jar,路徑:"android-sdks\extras\android\support\v4\android-support-v4.jar",將此import進專案裡。

xml代碼:

<Linearlayout xmlns:android="http://schemas.android.com/apk/res/android">
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
     >
    <Horizontalscrollview
        android:scrollbarSize="0dp"
        android:scrollbars="none"
        android:id="@+id/tab_view"
        android:layout_width="match_parent"
        android:layout_height="70dp">
        <Linearlayout
            android:id="@+id/my_tab"
            android:background="@android:color/white"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <Button
                android:text="Home"
                android:textColor="@android:color/black"
                android:id="@+id/home"
                android:gravity="center"
                android:background="@null"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"/>
            <Button
                android:text="Page1"
                android:textColor="@android:color/black"
                android:id="@+id/page1"
                android:background="@null"
                android:gravity="center"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"/>
            <Button
                android:text="Page2"
                android:textColor="@android:color/black"
                android:id="@+id/page2"
                android:background="@null"
                android:gravity="center"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"/>
       

   
    <Linearlayout
        android:background="@android:color/white"
        android:layout_width="match_parent"
        android:layout_height="5dp">
        <Imageview
            android:id="@+id/tab_bottom_line"
            android:background="#22a4d4"
            android:layout_width="50dp"
            android:layout_height="5dp"/>
       

    <android.support.v4.view.viewpager
        android:id="@+id/my_viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>



畫面主要分上下兩部分,上面為tag,下半部為每個tag的內容。

程式代碼:
class MyPagerAdapter extends PagerAdapter{
 private Context c;
        public MyPagerAdapter(Context context) {
            this.c = context;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {

            View v = mPagerViews.get(position);

            if(v.getParent() != null){
                ViewGroup vg = (ViewGroup)v.getParent();
                vg.removeView(v);
            }

            container.addView(v, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

            return v;
        }

        @Override
        public int getCount() {
            return mPagerViews.size();
        }


        @Override
        public boolean isViewFromObject(View v, Object o) {
            return v == o;
        }
    }

    private int mX;

    private void test2() {

        setContentView(R.layout.activity_main2);

        mTabLine = (ImageView)findViewById(R.id.tab_bottom_line);

        ViewGroup.LayoutParams _layoutP = mTabLine.getLayoutParams();

        _layoutP.width = mWidth/TabOnePageSize;
        mTabLine.setLayoutParams(_layoutP);

        LinearLayout _my_tab = (LinearLayout)findViewById(R.id.my_tab);
        _my_tab.removeAllViews();

        for(int index = 0; index < PageNumber; index++){
            TextView _layout = new TextView(this);
            _layout.setTextSize(100);
            _layout.setTextColor(Color.GRAY);

            String _page;
            int _color;
            if(index == 0){
                _page = "Page "+(PageNumber-2);
                _color = -21704*(PageNumber-2);
            }
            else if(index == PageNumber-1){
                _page = "Page "+1;
                _color = -21704;
            }
            else{
                _page = "Page "+index;
                _color = -21704*(index);

                Button _b = new Button(this);
                if(index == 1) _b.setTextColor(Color.parseColor(mTabColor));
                else _b.setTextColor(Color.BLACK);
                _b.setBackgroundColor(Color.WHITE);
                _b.setText(_page);
                _my_tab.addView(_b,mWidth / TabOnePageSize, ViewGroup.LayoutParams.MATCH_PARENT);
                mTabViews.add(_b);
                final int _index = index;
                _b.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(final View view) {
                        currentTabIndex = _index;
                        mViewPager.setCurrentItem(currentTabIndex,true);
                    }
                });
            }
            _layout.setText(_page);
            _layout.setBackgroundColor(_color);

            mPagerViews.add(_layout);
        }

        mPagerAdapter = new MyPagerAdapter(this);

        mViewPager = (ViewPager)findViewById(R.id.my_viewpager);
        mViewPager.setAdapter(mPagerAdapter);
        mViewPager.setOffscreenPageLimit(PageNumber);
        mViewPager.setCurrentItem(1);
        mViewPager.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if(motionEvent.getAction() == MotionEvent.ACTION_DOWN)
                {
                    mX = (int) motionEvent.getX();
                }
                else if(motionEvent.getAction() == MotionEvent.ACTION_MOVE)
                {

                    final int _move = (int) (mX - motionEvent.getX())/3;

                    TranslateAnimation _tranA = new TranslateAnimation(0,_move,0,0);
                    _tranA.setDuration(0);
                    _tranA.setAnimationListener(new Animation.AnimationListener() {
                        @Override
                        public void onAnimationStart(Animation animation) {

                        }

                        @Override
                        public void onAnimationEnd(Animation animation) {
                            mTabLine.clearAnimation();
                            mTabLine.layout(mTabLine.getLeft()+_move,mTabLine.getTop(),mTabLine.getRight()+_move,mTabLine.getBottom());
                        }

                        @Override
                        public void onAnimationRepeat(Animation animation) {

                        }
                    });

                    mTabLine.startAnimation(_tranA);

                    mX = (int) motionEvent.getX();
                }
                else if(motionEvent.getAction() == MotionEvent.ACTION_UP)
                {
                    scrollTabView();
                }
                return false;
            }
        });
        mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

            @Override
            public void onPageSelected(int pos) {

                currentTabIndex = pos;

                boolean _move_again = false;
                if(currentTabIndex == PageNumber -1)
                {
                    currentTabIndex = 1;
                    mViewPager.setCurrentItem(currentTabIndex,false);
                    _move_again = true;
                }
                else if(currentTabIndex == 0)
                {
                    currentTabIndex = PageNumber-2;
                    mViewPager.setCurrentItem(currentTabIndex,false);
                    _move_again = true;
                }

                for(int count = 0; count< mTabViews.size(); count++)
                {
                    int _now = currentTabIndex-1;
                    if(count == _now) ((Button)mTabViews.get(count)).setTextColor(Color.parseColor(mTabColor));
                    else ((Button)mTabViews.get(count)).setTextColor(Color.BLACK);

                }

                if(!_move_again) scrollTabView();

                preTabIndex = currentTabIndex;
            }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {

            }

            @Override
            public void onPageScrollStateChanged(int arg0) {

            }
        });
    }

    private void scrollTabView() {

        HorizontalScrollView _tab_view = (HorizontalScrollView)findViewById(R.id.tab_view);

        int _move = mWidth*((currentTabIndex-1)/TabOnePageSize);

        _tab_view.smoothScrollTo(_move, 0);


        int page = currentTabIndex-1;
        if(_move == ((PageNumber-2)/TabOnePageSize*mWidth)) page+=(PageNumber%3);
        final int _XPosition = (page)%3*(mWidth/TabOnePageSize);
        int _Xmove = _XPosition - mTabLine.getLeft();

        TranslateAnimation _tranA = new TranslateAnimation(0,_Xmove,0,0);
        _tranA.setDuration(0);
        _tranA.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                mTabLine.clearAnimation();
                mTabLine.layout(_XPosition,mTabLine.getTop(),_XPosition+mTabLine.getWidth(),mTabLine.getBottom());
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        mTabLine.startAnimation(_tranA);

    }



循環概念:
初始化ViewPager時在頭/尾各多加最後一頁/第一頁,讓使用者可以在第一頁時再往左滑到最後一頁;反之如此。除此外只要偵測touch event發生後page切換到哪一頁,再scroll tag bar就能達到tag與page搭配的循環換頁效果。

4.10.2014

Android : WebView 自適螢幕大小

讓WebView裡面的內容自動調成螢幕大小有以下兩種方法 :

第一種方法,啟動viewport的meta標籤:

WebView.getSettings().setUseWideViewPort(true);
WebView.getSettings().setLoadWithOverviewMode(true);

setUseWideViewPort() : 參數可設定true或是false,設定true時會將viewport的meta tag啟動,而meta tag又是甚麼呢?在W3C協議裡"This section is not normative.",並沒有強制規範,不過Android的API說明有提到"When the value is true and the page contains the viewport meta tag, the value of the width specified in the tag is used. If the page does not contain the tag or does not provide a width, then a wide viewport will be used." 如果我們沒有強制設定寬度,那麼就會使用可是範圍的最大視野,其意思就是螢幕的大小。
setLoadWithOverviewMode() : 參數值可設定true或是false,默認值是false,此參數值設定為true是為了當內容大於viewport時,系統將會自動縮小內容以適屏幕寬度.


第二種方法:

WebView.getSettings().setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN);

有些部落客有寫到這種方法,但官方表示"This algorithm is now obsolete."此種方法已過時了,也許在某些版本可以正常使用,但就我最近在最新的4.4.2版本上跑,它並沒有自是螢幕大小,因此我會排除此方法,所以在這我就不多贅述了.

2.20.2013

PPT slideshow on Android

前陣子在研究如何在Android平台上寫一個播放PPT檔案的APK。
找了很久只找到POI Library(http://poi.apache.org/),這是一切噩夢的開始。

備註 : 我的作法是把每一張幻燈片轉成一張jpg檔,然後在淡入淡出播放,目前也還沒看到可以播放幻燈片動畫的apk。p.s.如果有的話請與分享,感謝

POI Library中有用到純java裡的awt package (http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/package-summary.html) 這個包正巧android沒有收入(我想它不收入也是正確的,它有自己一套的作圖法),為了解決這頭痛的原因,在網路上翻來覆去的找,總算讓我找到替代的方案,不是我在說網友們真的很厲害,就是這LGame (http://blog.csdn.net/cping1982/article/details/6069738) 解決了我大半的困擾,當然自己也可以去實現所需要的interface,但既然有現成的為何不用呢!

7.12.2012

Android Api : How to get the system environment variables


System.getenv(String name) : Returns the value of the environment variable with the given name .

Parameter :  name -> the name of the environment variable.


How to know all of the environment variables:

Enter adb shell command line . Then key in "export" and you will get all of the environment variables.
By the way, if you want to add a environment variable,you just key in "exprot variable-name="variable-value".