-->

分類

2016年3月31日 星期四

Android ViewPager


來做個一頁可以有三個page然後換頁用翻的transform
MainActivity
public class MainActivity extends AppCompatActivity implements ScreenSlidePageFragment.OnFragmentInteractionListener{
    private static final int NUM_PAGES = 5;
    private float widthScale = 0.6f;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mPager = (ViewPager) findViewById(R.id.pager);
        mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
        mPager.setAdapter(mPagerAdapter);
        //mPager.setPageTransformer(false, new ZoomOutPageTransformer());
        mPager.setPageTransformer(false, new FlipPageTransformer());

        windowWidth = width;
        //mPager.setPageMargin(-(int)getResources().getDimension(R.dimen.viewpager_margin));

        //set page margin to be half of (viewpager's width - page's width)
        //page's width should be widthScale * viewPagerWidth
        float viewPagerWidth = getResources().getDimension(R.dimen.viewpager_width);
        mPager.setPageMargin((int) ((1.0f - widthScale) * viewPagerWidth / 2.0f));

        mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                if(position == NUM_PAGES - 1) {
                    //avoid to get stuck at last page
                    mPager.setCurrentItem(NUM_PAGES - 2, true);
                    return;
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        };


        mPager.addOnPageChangeListener(mOnPageChangeListener);
    }

    private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
        public ScreenSlidePagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            final ScreenSlidePageFragment returnFragment;
            if(position % 2 == 0){
                String arg0 = "even";
                String arg1 = "isLast";
                if(position == NUM_PAGES - 1){
                    returnFragment = ScreenSlidePageFragment.newInstance(arg0, arg1);
                } else {
                    returnFragment = ScreenSlidePageFragment.newInstance(arg0, "");
                }

            } else {
                String arg0 = "odd";
                String arg1 = "isLast";
                if(position == NUM_PAGES - 1){
                    returnFragment = ScreenSlidePageFragment.newInstance(arg0, arg1);
                } else {
                    returnFragment = ScreenSlidePageFragment.newInstance(arg0, "");
                }
            }

            //return new ScreenSlidePageFragment();
            return returnFragment;
        }

        @Override
        public int getCount() {
            return NUM_PAGES;
        }


        @Override
        public float getPageWidth(int position) {
            return widthScale;
        }


    }

    public class FlipPageTransformer implements ViewPager.PageTransformer {
        private static final float MIN_SCALE = 0.85f;
        private static final float MIN_ALPHA = 0.5f;

        public void transformPage(View view, float position) {
            int pageWidth = view.getWidth();
            int pageHeight = view.getHeight();
            float pageFullWidth = (float)pageWidth / widthScale;
            //I guess this the page's movement from 1 -> 0 or 0 -> -1 will travel the distance of (pageFullWidth - (float)pageWidth) / 2.0f...
            float movement = (pageFullWidth - (float)pageWidth) / 2.0f;
            //float approx equal threshold
            float threshold = 0.0005f;
            if("isLast".equals(view.getTag())){
                //do not display last page
                view.setAlpha(0);
                return;
            }
            if (position < -1) { // [-Infinity,-1)
                // This page is way off-screen to the left.
                view.setAlpha(0);

            } else if (position <= 1) { // [-1,1]
                view.setAlpha(1);

                //pageFullWidth * -position: move page's left edge to align viewpager's left edge
                //(1.0f + position) * movement: move page back to viewpager's left edge(if -1 < position < 0)
                // or view pager's right edge(if 0 < position < 1), but we still need page margin to help...
                view.setTranslationX(pageFullWidth * -position + (1.0f + position) * movement);

                if(position < -threshold){
                    view.setPivotX(0);
                    view.setRotationY(-position * 90.0f);
                } else if(position > threshold){
                    view.setPivotX(pageWidth);
                    view.setRotationY(-position * 90.0f);
                } else {//position == 0.0f
                    view.setPivotX(0);
                    //make page to be at center of viewpager
                    view.setTranslationX(movement);
                    view.setRotationY(0);
                }



            } else { // (1,+Infinity]
                // This page is way off-screen to the right.
                view.setAlpha(0);
            }
        }
    }

    @Override
    public void onFragmentInteraction(Uri uri) {

    }
}

ScreenSlidePageFragment
public class ScreenSlidePageFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";
    public static final String VIEW_TAG_LAST = "last";

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;

    private OnFragmentInteractionListener mListener;

    public ScreenSlidePageFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment ScreenSlidePageFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static ScreenSlidePageFragment newInstance(String param1, String param2) {
        ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        //return inflater.inflate(R.layout.fragment_screen_slide_page, container, false);
        View view = inflater.inflate(R.layout.fragment_screen_slide_page, container, false);
        if("even".equals(mParam1)){
            view.setBackgroundColor(getResources().getColor(R.color.primary_purple));
        }
        if("isLast".equals(mParam2)){
            view.setTag("isLast");
        }
        return view;
    }

    // TODO: Rename method, update argument and hook method into UI event
    public void onButtonPressed(Uri uri) {
        if (mListener != null) {
            mListener.onFragmentInteraction(uri);
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    /**
     * This interface must be implemented by activities that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other fragments contained in that
     * activity.
     * * See the Android Training lesson Communicating with Other Fragments for more information.
     */
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void onFragmentInteraction(Uri uri);
    }
}

res/layout/activity_main.xml

    


res/layout/fragment_screen_slid_page.xml

    



res/values/strings.xml

    AAA
    Hello blank fragment


res/values/colors.xml

    #3F51B5
    #303F9F
    #FF4081
    #52bf90
    #398564
    #FF4081
    #e4e5e5
    #e0ac69
    #c68642

    #673AB7
    #512DA8
    #D1C4E9
    #FF9800
    #212121
    #727272
    #FFFFFF
    #B6B6B6
    #374046


res/values/dimens.xml

    
    16dp
    16dp

    240dp

2016年3月27日 星期日

Unity Shader

分兩種: Surface Shader及vert, frag
Surface最後還是會compile成vert, frag, Surface就是比較總合的一種寫法
Surface的語法定義
surface必須照這方式宣告:
#pragma surface surfaceFunction lightModel [optionalparams]

lightModel:定義和光源互動的規則
lightModel宣告
LightModel宣告規則
見Lighting Model declaration
#pragma surface surfaceFunction LightModelName [optionalparams]
half4 LightingLightModelName (SurfaceOutput s, half3 lightDir, half atten);

Surface要定義vert, frag:
#pragma surface surf Lambert vertex:vert finalcolor:mycolor
void vert (inout appdata_full v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input,o); o.customColor = abs(v.normal); }
void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) { color *= _ColorTint; }

2016年3月24日 星期四

Android OverScroller, SplineScroller

source code
SplinerScroller做了一個類似在算Cubic Bezier Curve的東西, 只是這邊他不是算B(t)的值而是從已知B(x)的值去算t, 這裡利用t是strictly ascending的特性所以x min可以不用reset而x max需每次reset成1.0, 利用x = (x min + x max) / 2代入cubic bezier curve去看B(x)是否夠接近所求之值, 不夠則根據算出的B(x)修改x min, x max, 求出之各x存起來做為Spline interpolation之用(這段就看不太懂了)
(Wiki裡的t就是x)
Wiki Bezier

static {
            float x_min = 0.0f;
            float y_min = 0.0f;
            for (int i = 0; i < NB_SAMPLES; i++) {
                final float alpha = (float) i / NB_SAMPLES;

                float x_max = 1.0f;
                float x, tx, coef;
                while (true) {
                    x = x_min + (x_max - x_min) / 2.0f;
                    coef = 3.0f * x * (1.0f - x);
                    tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x;
                    if (Math.abs(tx - alpha) < 1E-5) break;
                    if (tx > alpha) x_max = x;
                    else x_min = x;
                }
                SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x;

                float y_max = 1.0f;
                float y, dy;
                while (true) {
                    y = y_min + (y_max - y_min) / 2.0f;
                    coef = 3.0f * y * (1.0f - y);
                    dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y;
                    if (Math.abs(dy - alpha) < 1E-5) break;
                    if (dy > alpha) y_max = y;
                    else y_min = y;
                }
                SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y;
            }
            SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f;
        }

實際使用OverScroller時就是view在computeScroll時不斷invalidate直到scroller認為scroll結束

@Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            // This is called at drawing time by ViewGroup.  We use
            // this method to keep the fling animation going through
            // to completion.
            int x = mScroller.getCurrX();
            int y = mScroller.getCurrY();

            scrollTo(x, y);

            // Keep on drawing until the animation has finished.
            postInvalidate();
        }
    }

2016年3月21日 星期一

Android touch, customized scroll

link

requestDisallowTouchIntercept: child阻止parent繼續intercept touch event,使用時機: drag, cleared when ACTION_CANCEL
interceptTouchEvent: parent收到touch event決定是否要再dispatch給children, 使用時機: scroll

Activity.onTouchEvent: 如果touch event都沒有被consumed, 最後會回到Activity.onTouchEvent

Touch Slop: scroll和touch的分界值

---------------
customized scroll

code link
大陸人sample

2016年3月17日 星期四

unity android plugin

link

unity doc

Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes

sdk path : Edit -> Preference -> Externel tool -> Android
C:/Users/XXX/AppData/Local/Android/sdk

Android build:
exportJar: in gradle tasks "other"
built jar: in ProjectName/app/release

trouble shooting:

just copy Android manifest to Unity
delete Theme light, Theme Dark related resources
copy (Android) app/res to (Unity)Plugins/Android
remove unused lib(jar) in Android

一、编译apk时。================================================
1.编译输出apk时报错。
Error building Player: CommandInvokationFailure: Failed to re-package resources. See the Console for details.
D:\android-sdk-windows\build-tools\19.1.0\aapt.exe package --auto-add-overlay -v -f -m -J gen -M AndroidManifest.xml -S "res" -I "D:/android-sdk-windows/platforms/android-21\android.jar" -F bin/resources.ap_
stderr[
res\values\styles.xml:7: error: Error retrieving parent for item: No resource found that matches the given name 'Theme.AppCompat.Light'.
res\values-v11\styles.xml:7: error: Error retrieving parent for item: No resource found that matches the given name 'Theme.AppCompat.Light'.
res\values-v14\styles.xml:8: error: Error retrieving parent for item: No resource found that matches the given name 'Theme.AppCompat.Light.DarkActionBar'.
]
答:在styles.xml没有 'Theme.AppCompat.Light 和 Theme.AppCompat.Light.DarkActionBar 这两个皮肤。。。
android 皮肤的使用 在这里。。
http://developer.android.com/guide/topics/ui/themes.html
或者改为:
<resources>
    <!--
        Base application theme, dependent on API level. This theme is replaced
        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
    -->
    <style name="AppBaseTheme" parent="android:Theme.Light.NoTitleBar">
        <!--
            Theme customizations available in newer API levels can go in
            res/values-vXX/styles.xml, while customizations related to
            backward-compatibility can go here.
        -->
    </style>
    <!-- Application theme. -->
    <style name="AppTheme" parent="AppBaseTheme">
        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
    </style>
</resources>
这样也可解决。

2、如果Unity打包过程出现类似下面这样的错误提示:
     Error building Player: CommandInvokationFailure: Failed to re-package resources. See the Console for details.
     X:\Android SDK\adt-bundle-windows-x86-XXXXXX\sdk\build-tools\X.1.0\aapt.exe package --auto-add-overlay -v -f -m -J gen -M AndroidManifest.xml -S "res" -I "E:/Android SDK/adt-bundle-windows-x86-XXXXXX/sdk/platforms/android-X\android.jar" -F bin/resources.ap_
表示在Android外挂项目里的res文件夹里有的地方的xml设置文件里的设置内容找不到,比如一条设置指向一个名为bnt的png图片文件,但这个图片的名字不是这个。

    3、如果Unity打包过程出现类似下面这样的错误提示:
    Error building Player: CommandInvokationFailure: Unable to convert classes into dex format. See the Console for details.
    X:\Program Files\Java\jdk1.X.X_XX\bin\java.exe -Xmx1024M -Dcom.android.sdkmanager.toolsdir="X:/Android SDK/adt-bundle-windows-x86-20130729/sdk\tools" -Dfile.encoding=UTF8 -jar "X:/Program Files/Unity/Editor/Data/BuildTargetTools/AndroidPlayer\sdktools.jar" -
表示你的你复制过来的libs文件夹里的东西和Unity的有冲突,通常是含有和Unity的classes.jar相同内容的jar文件。
二、安装后运行报错。================================================================
1.如果单独在运行没问题,接到Unity打包后在Android机子上一运行就退出,特别是那些拿来demo修改一下就用的人好碰到这种情况。检查代码都没有问题。这种情况原因很多,有一个可能性很大,就是在继承UnityPlayerActivity的那个Activity里用了finish函数,把这句删掉就行了,因为继承了UnityPlayerActivity就是Unity程序了,设置时还让它和Unity一起启动的,finish它,就是把你的游戏程序finish了。





2016年3月15日 星期二

Android import local aar

stackoverflow

在project folder
File > New > New module and choose Import .JAR/.AAR Package.
此時AAR project會產生在跟app同層並且此aap project的build.gradle會寫上
configurations.create("default")
artifacts.add("default", file('this-is-yours-package-in-aar-format.aar'))

此時在app的build.gradle的dependencies裡加上
compile project(':name-of-module-created-via-new-module-option-described-above')

Unity infinite scrolling

unity package

模仿Android list view及 Adapter模式及覆寫ScrollRect做的UGUI infinite scoll, 目前還懶得打說明...

Android VelocivtyTracker

VelocityTracker.cpp

裡面的LeastSquaresVelocityTrackerStrategy很有趣
我感覺是類似利用位移的物公式x = x0 + v * t + c * a * (t^2) (x為總位移, x0為某常數, t為時間,v為速度, c為某常數,a為加速度,以degree為2的情況來看)
就是利用之前記錄下的movement及time去建立一個矩陣去解least square(解x0 , v, c*a)
主要的解釋在這裡:
* That is to say, the function that generated the input data can be approximated
* by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
*
* The coefficient of determination (R^2) is also returned to describe the goodness
* of fit of the model for the given data.  It is a value between 0 and 1, where 1
* indicates perfect correspondence.
*
* This function first expands the X vector to a m by n matrix A such that
* A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then
* multiplies it by w[i]./
*

而其中y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
這裡y(x)就是位移(movement), x是時間, degree只有2所以就是解y(x) ~= B[0] + B[1] x + B[2] x^2
再對照x = x0 + v * t + c * a * (t^2)可知 B[1]就是v, 所以在最後return velocity時
(VelocityTracker::getVelocity裡)為 *outVx = estimator.xCoeff[1];

註解裡面的w[i]是權重,這個就還沒仔細研究, 不過權重目的應該是突顯某個B[i]的重要性

當然以上都只是我的感覺啦

2016年3月9日 星期三

Android Activity main thread entry point

一個好解說

Android 5 ActivityThread source code

Unity UI notes

UGUI Virtual Joystick (implementing EventSystem interfaces)

Android workaround

java.lang.IllegalArgumentException: Service Intent must be explicit

cause source code (in ContextImpl.java)

private void validateServiceIntent(Intent service) {
        if (service.getComponent() == null && service.getPackage() == null) {
            if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                IllegalArgumentException ex = new IllegalArgumentException(
                        "Service Intent must be explicit: " + service);
                throw ex;
            } else {
                Log.w(TAG, "Implicit intents with startService are not safe: " + service
                        + " " + Debug.getCallers(2, 3));
            }
        }
    }
solution

2016年3月8日 星期二

Web tools

www.web2generators

Code block in Blogger(By SyntaxHighlighter)

ㄟ找來找去發現還是以下這篇有用

連結

不過要注意這連結裡用的theme是Emacs如果要用default theme記得要換一下

<!--<link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeEmacs.css' rel='stylesheet' type='text/css'/>-->

<link href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/>

2016年3月7日 星期一