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搭配的循環換頁效果。