Design Support Library: Coordinator layout and Behavior

There are some examples for coordinator layout move floating action button upward while snackbar showing, but do you ever curious about how the whole layout interact with snackbar?
This is the example for the case. Just show how we can use Behavior to customize view intraction we want. Or, be more creative, you may have different needs.

Screenshot.gif

Step 1. Implement new behavior

It's needed to check if the operating device supports snackbar.

public class MoveUpwardBehavior extends CoordinatorLayout.Behavior<View> {
    private static final boolean SNACKBAR_BEHAVIOR_ENABLED;

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
        child.setTranslationY(translationY);
        return true;
    }

    static {
        SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
    }
}

Step 2. Implement custom view so that we can apply MoveUpwardBehavior to it.

In this case, we make the whole LinearLayout interact with snackbar.
It's very easy, as here said, just pass the class to DefaultBehavior annotation.

@CoordinatorLayout.DefaultBehavior(MoveUpwardBehavior.class)
public class CustomLinearLayout extends LinearLayout {
    public CustomLinearLayout(Context context) {
        super(context);
    }

    public CustomLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

Step 3. Almost Done!

Add CustomLinearLayout into layout. Remember, it should be included by CoordinatorLayout!

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.example.alisondemo.musicgenre.CustomLinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        ...
     </com.example.alisondemo.musicgenre.CustomLinearLayout>
</android.support.design.widget.CoordinatorLayout>

But....why we don't even have to implement behavior for FAB?

Form the source code of android.support.design.widget.FloatingActionButton class, as you can see:

@DefaultBehavior(FloatingActionButton.Behavior.class)
public class FloatingActionButton extends ImageView {
...

Yes, it actually has it's own Behavior implemented.

Conclusion

We can implement any kind of behavior we want in any views. It should be very interesting. :)

Full source code is now on GitHub

https://github.com/Alishuang/MusicGenre

Using am to start service with extras

For testing(or lazy ; ) ) purpose, sometimes we need to start service by typing am (Activity Manager) command directly.

Look up "am" usage, and as you can see...

am startservice [--user <USER_ID> | current] **<INTENT>**  

The thing is, how to construct ?

To be more specific,

am startservice -n [Package Name]/[Class Name]  

You could also attache extras with this intent by adding "-es [Extra Key] [Extra Value]"

For example,

am startservice -n com.test.demoservice/com.test.demoservice.DemoService -es 
path /storage/sdcard/test.txt -es count 8  

Setting System Property

If you want to add/modify some system properties, it's an good idea to use SystemProperties.get & SystemProperties.set.

Note that the application should share the system uid, and have the signature of platform.

This example is creating a new system properties which would persist even if device reboot. Unless you erase User Data partition (such as performing factory reset)

1.AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="com.example.demomodesettingstest"  
    android:versionCode="1"  
    android:versionName="1.0"  
  **  android:sharedUserId="android.uid.system" **>

2.Android.mk

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)


LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_PACKAGE_NAME := DemoApp

**LOCAL_CERTIFICATE := platform**

include $(BUILD_PACKAGE)

3.MainActivity.java

import android.os.SystemProperties; 
  
public class MainActivity extends Activity {  
  
      //Add in somewhere  
      SystemProperties.set("persist.sys.demo", "1");
}  

After these three steps, you can find the property "persist.sys.demo" is
added in /data/property

Note:

  1. About naming property, please follow the prefix that defined in system/core/init/property_service.c

     property_perms[] = {
        { "net.rmnet0.",      AID_RADIO,    0 },
        { "net.gprs.",        AID_RADIO,    0 },
        { "net.ppp",          AID_RADIO,    0 },
        { "net.qmi",          AID_RADIO,    0 },
        { "net.lte",          AID_RADIO,    0 },
        { "net.cdma",         AID_RADIO,    0 },
        { "ril.",             AID_RADIO,    0 },
        { "gsm.",             AID_RADIO,    0 },
        { "persist.radio",    AID_RADIO,    0 },
        { "net.dns",          AID_RADIO,    0 },
        { "sys.usb.config",   AID_RADIO,    0 },
        { "net.",             AID_SYSTEM,   0 },
        { "dev.",             AID_SYSTEM,   0 },
        { "runtime.",         AID_SYSTEM,   0 },
        { "hw.",              AID_SYSTEM,   0 },
        { "sys.",             AID_SYSTEM,   0 },
        { "service.",         AID_SYSTEM,   0 },
        { "wlan.",            AID_SYSTEM,   0 },
        { "persist.wlan.",    AID_SYSTEM,   0 },
        { "bluetooth.",       AID_BLUETOOTH,   0 },
        { "dhcp.",            AID_SYSTEM,   0 },
        { "dhcp.",            AID_DHCP,     0 },
        { "debug.",           AID_SYSTEM,   0 },
        { "debug.",           AID_SHELL,    0 },
        { "log.",             AID_SHELL,    0 },
        { "service.adb.root", AID_SHELL,    0 },
        { "service.adb.tcp.port", AID_SHELL,    0 },
        { "persist.sys.",     AID_SYSTEM,   0 }, -> I chose this one.
        { "persist.service.", AID_SYSTEM,   0 },
        { "persist.security.", AID_SYSTEM,   0 },
        { "persist.service.bdroid.", AID_BLUETOOTH,   0 },
        { "selinux."         , AID_SYSTEM,   0 },
        { "modem.",            AID_RADIO,   0},
        { "media.",            AID_MEDIA,   0},
        { "persist.camera.front.freq", AID_SYSTEM,   0 },
        { "persist.camera.usb.freq", AID_SYSTEM,   0},
        { "sf.lcd_density",   AID_SYSTEM,   0 },
        { "persist.sys.cam",   AID_MEDIA,   0},
        { "media.", AID_MEDIA, 0},
        { NULL, 0, 0 }
    
  2. There are some property file other than /data/property

#define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"  
  
#define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"  
  
#define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"  
  
#define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"  
  
#define PROP_PATH_FACTORY          "/factory/factory.prop"  

Take a look if you're interested in it.

在Ubuntu使用adb

所需檔案:

1.adb

2./etc/udev/rules.d/51-android.rules

3.~/.android/adb_usb.ini

少一個檔案,下 "adb devices" command都會顯示找不到device

步驟

1.把adb放在~/bin底下

2.新增51-android.rules檔案,內容為

SUBSYSTEM="usb", ATTR{idVendor}=="12f3", ATTR{idProduct}=="5a67", MODE=="0666", 
OWNER="alison"

其中idVendor和idProduct要如何取得:

用usb將device與ubuntu接上後,下指令

~$lsusb

Bus 001 Device 001: ID 12f3:5a67 Brand-Name


12f3:5a67-->分別為idVendor:idProduct

再將這兩個值填到51-android.rules適當位置即可

另外,最後面的OWNER是optional,如要在su底下使用,則不需要這個attribute, 如要指定為某個user使用,就換成他的user name

3.到~/.android/底下看有無adb_usb.init,沒有就自已新增,有的話看一下裡面的內容

這支檔案的內容很簡單,只有一個idVendor,以上一步取得的idVendor為例,內容應為

0x12f3

4.現在可以用adb devices看有無找到device, 有的話就成功了!

Facebook plugin with fluid width

Due to the inline attribute causes the width of facebook plugin to be fix in a specific size. To solve this problem, use !important to override it.

Take fb like box for example:

If embedded code is:

<div id="fb-root"></div>  
<script>(function(d, s, id) {  
  var js, fjs = d.getElementsByTagName(s)[0];  
  if (d.getElementById(id)) return;  
  js = d.createElement(s); js.id = id;  
  js.src = "//connect.facebook.net/zh_TW/all.js#xfbml=1";  
  fjs.parentNode.insertBefore(js, fjs);  
}(document, 'script', 'facebook-jssdk'));</script>  
<div class="fb-like-box" data-href="https://www.facebook.com/pages/[your page]/[id]" data-show-faces="false" 
data-stream="true" data-header="false"></div>  

However, the most useful way is adding below in CSS

.fb-like-box, .fb-like-box span, .fb-like-box span iframe[style] {  
  width: 100% !important;  
}  

Then it works!

Web

Widget Tips

寫widget, 可以選擇寫出能resize的widget, 在Design Guidelines可以下載for 4.0的templates pack

NOTE: android:updatePeriodMillis這個屬性用的時後要很小心,因為device睡著的時後,依然會被這個叫醒去update,如果一小時超過一次update,就選擇用Alarm manager的elapsed_realtime/RTC定時去update,
因為Alarm manager是不會把睡著的device叫醒的。

Set the alarm type to either [ELAPSED_REALTIME][1]or [RTC][2], which will
only deliver the alarm when the device is awake. Then set updatePeriodMillis to
zero ("0").

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"  
android:minWidth="294dp"-->就是初始化的大小  
android:minHeight="72dp"-->所以minResizeHeight,minResizeWidth都可以比他們小  
android:updatePeriodMillis="86400000"  
android:previewImage="@drawable/preview"-->顯示在選擇widget畫面的預覽  
android:initialLayout="@layout/example_appwidget"  
android:configure="com.example.android.ExampleAppWidgetConfigure" -->即當加入widget時,會launch的activity, 是拿來設定widget用的  
android:resizeMode="horizontal|vertical"-->可調整大小的方向,再搭配minResizeHeight,minResizeWidth  
android:widgetCategory="home_screen|keyguard"-->keyguard指的是放在解鎖畫面上的widget  
android:initialKeyguardLayout="@layout/example_keyguard"></appwidget-provider>

Custom fontface (Font Family)

First, put xxx.ttf into asset dir.

Typeface tpf = Typeface.createFromAsset(context.getAssets(), "xxx.ttf");

text_view.setTypeface(tfs);

客製化UI元件(1) Custom UI components - disable sliding function of VeiwPager

如果想改變他的預設動作,那麼就可以不要執行他的super,就可以自訂在某些時後要做啥些事。

以ViewPager這個control為例,如果想把swipe then change page這件事改掉,可以new一個class去extend ViewPager,

package com.alison.test

pubilc class CustomViewPager extends ViewPager{

     private boolean value;
     public CustomViewPager(Context context, AttributeSet attrs){
          super(context,attrs);
          this.value = false;
     }
     
     // override some method
     public boolean onTouchEvent(MotionEvent event){
         // do some customization
          if(value){
               return super.onTouchEvent(event);
          }
          return false
     }
}

xml file

<com.alison.test.CustomViewPager 
     android:id="@+id/customPager
     ...
/>

java file that call it

CustomViewPager mViewPager = (CustomViewPager)findViewById(R.id.customPager); 
More detail -- [http://developer.android.com/training/custom-views/create-view.html][1] 

客製化UI元件(2) Custom UI components- double thumbs seek bar

這次遇到的是比較棘手的,因為考慮到傳入值的關系,所以就使用了泛型類別,

泛型要注意的是轉換的介面,有沒有轉對型態。

這次客製化的是double thumbs的seek bar,

從網路上找到一個example拿來用,也依實際需求改了一下code

在這支code裡可以學的東西有滿多的,介紹一下吧!!

Basic Concept

一開始要有一個概念,在UI上的control不管是誰,都是用「畫」出來的,

所以這個class是繼承ImageView,主要實作2個method : onDraw() -> 呈現什麼在畫面上,  onTouchEvent()
->接event到自訂的listener

Implementation

canvas)``` 畫在canvas上,
  

 ```onTouchEvent(MotionEvent event)``` 把收進來的event,去對應到要做的事,

MotionEvent.ACTION_DOWN , MotionEvent.ACTION_MOVE, MotionEvent.ACTION_UP``` ** ** **自訂listener** RangeSeekBar.java 1.定義interface [![][1]][1] 2.宣告使用時就會是 OnRangeSeekBarChangeListener listener; 3.將listener與touchEvent結合,即為:
public boolean onTouchEvent(MotionEvent event){

     switch(event.getAction() & MotionEvent.ACTION_MASK){//這邊還沒弄懂mask的作用是什麼 

     case MotionEvent.ACTION_DOWN:
               ...
     case MotionEvent.ACTION_MOVE:
               ...
               listener.onRangeSeekBarValuesChanged(bar,minvalue,maxvalue); 
               break;
     case MotionEvent.ACTION_UP:
               ...
               listener.onStopRangeSeekBarValuesChanged(bar,minvalue,maxvalue); 
               break;
     ....
     }

4.定義listener的setter

     public void setOnRangeSeekBarChangeListener(OnRangeSeekBarChangeListener<T> 
listener){
          this.listener = listener;
     }

使用自定元件時,

1.外部呼叫使用的時後,

RangeSeekBar<Integer> rangeSeekBar;
rangeSeekBar = new RangeSeekBar<Integer>();
rangeSeekBar.setOnRangeSeekBarChangeListener(new OnRangeSeekBarChangeListener(){ 
     @Override
     public void onRangeSeekBarValuesChanged(){
     ...
     }

     @Override
     public voidonStopRangeSeekBarValuesChanged(){
     ...
     }
});

Android Fragment

 Fragment是3.0後才產生的UI component

以下以一個實作的例子來介紹frgment

要形成一個有fragment的UI,在此例中,主要有3個組成元件,

  • 1個activity
  • 2個fragment

這兩個fragment就是要放在activity上的,所以在activity的layout中,就得宣告這兩個fragment和它相對應的class

main.xml

而這兩個fragment一定要繼承fragment才行,有4種:

  • Fragment
  • DialogFragment
  • ListFragment
  • PreferenceFragment

這裡用到Fragment和PreferenceFragment

其實他們也個自對到一個layout的xml

要注意的是,繼承 PreferenceFragment的xml需要用PreferenceScreen包起來,並且使用他底下的元件。

  • CheckBoxPreference
  • ListPreference
  • PreferenceCategory
  • ...等,此外,我的eclipse(Indigo)認不出fragment, 所以在佈置UI的時後,無法用托拉的方式。

preference.xml

繼承PreferenceScreen時,在onCreate()裡需要使用addPreferencesFromResource(R.layout.preferences);

繼承Fragment時,要實作onCreateView

在fragment.xml檔裡,不限UI的擺設方式,只是簡單的秀字也行。

以UI呈現部份,到此即可。