Android開發基礎知識整理之UI與Fragment

本篇主要涉及Android中UI開發和碎片的使用。

一、 UI開發

(一) 常見控件的使用

1. TextView

  • android:gravity 指定文字對其方式,可選值有top、bottom、left、right、center等。

2. Button

  • 匿名類方式註冊監聽器
button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
                // 相應邏輯
        }
});
  • 實現接口方式註冊監聽器
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(bundle savedInstanceState) {
        ...
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                // 相應邏輯
                break;
            default:
                break;
        }
    }
}

3. EditText

  • android:hint 設置提示性文本
  • android:maxLines 設置最大行數

  • 獲取輸入的內容:

    String inputText = editText.getText().toString();

4. ImageView

  • android:src="@drawable/img_1" 指定圖片
  • 動態更改圖片:
imageView.setImageResource(R.drawable.img_2);

5. ProgressBar

  • android:visibility 設置控件的可見屬性,默認visible 表示可見,invisible 表示不可見但佔據着原來的位置(透明狀態),gone 表示不可見且不佔用任何屏幕空間。
  • 通過代碼設置可見屬性:
if (progressBar.getVisibility() ==View.VISIBLE) {
    progressBar.setVisibility(View.GONE);
}
  • 通過style屬性可以指定不同樣式。style="?android:attr/progressBarStyleHorizontal"
  • 通過 android:max="100" 給進度條設置一個最大值,然後可在代碼中動態更改進度:
int progress = progressBar.getProgress();
progress = progress + 10;
progressBar.setProgress(progress);

6. AlertDialog

  • 通過AlertDialog.Builder創建一個AlertDialog實例,然後可以設置標題、內容、可否取消等屬性,接下來調用 setPositiveButton() 方法爲對話框設置確定按鈕的點擊事件,調用 setNegativeButton() 方法設置取消按鈕的點擊事件,最後調用 show() 方法將對話框顯示出來。
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("這是標題");
dialog.setMessage("重要內容或警告信息");
dialog.setCancelable(false);
dialog.setPositiveButton("OK", new dialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    }
});
dialog.setNegativeButton("Cancel", new DialogInterface.OnClickLister() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    }
});
dialog.show();

7. ProgressDialog

  • 與AlertDialog類似,也是構建一個ProgressDialog對象,然後設置標題、內容、可否取消等,最後通過 show() 方法顯示出來。
  • 調用 dismiss() 方法關閉對話框;

(二) 四種基本佈局

1. 線性佈局 (LinearLayout)

  • android:orientation 指定排列方向,可選vertical或horizontal;
  • android:layout_gravity 指定控件在佈局中的對齊方式,可選值有top、bottom、center_viertical、left、right等;
  • android:layout_weight 可以按比例控制控件大小;

2. 相對佈局 (RelativeLayout)

  • android:layout_alignParentLeftandroid:layout_alignParentRightandroid:layout_alignParentTopandroid:layout_alignParentBottom
    android:layout_centerInParent 屬性設置控件在父控件中的位置。
  • android:aboveandroid:belowandroid:toLeftOfandroid:toRightOf 設置控件間的相對位置。

3. 幀佈局 (FrameLayout)

  • 所有控件默認擺放在佈局左上角,可使用 android:layout_gravity 設置對齊方式。

4. 百分比佈局

  • 包括PercentFrameLayout和PercentRelativeLayout,包含在 com.android.support:percent 庫中。
  • app:layout_widthPercentapp:layout_heightPercent 可直接指定控件在佈局中所佔百分比。
<Button
    android:id="@+id/button1"
    android:text="Button 1"
    android:layout_gravity="left|top"
    app:layout_widthPercent="50%"
    app:layout_heightPercent="50%" />

(三) 自定義控件

1. 引入佈局

構建一個佈局文件,在主活動的佈局中使用 <include layout="@layout/title" />引入。

2. 創建自定義控件

  • 新建TitleLayout繼承自LinearLayout,重寫構造方法,藉助LayoutInflater對自定義佈局進行動態加載。
    • from(context) 方法構建出LayoutInflater對象
    • inflate(要加載的佈局文件id, 父佈局) 進行動態加載
  • 加入相應邏輯。
  • 在佈局文件中添加,需要指明完整類名。
public class TitleLayout extends LinearLayout {

    public TitleLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        LayoutInflater.from(context).inflate(R.layout.title, this);
        Button titleBack = (Button) findViewById(R.id.title_back);
        titleBack.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                ((Activity) getContext()).finish();
            });
    }

}
<com.example.uicostomviews.TitleLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

(四) ListView

1. 基本用法

藉助適配器ArrayAdapter將數據傳遞給ListView:

  • ArrayAdapter構造函數中傳入三個參數 (當前上下文、ListView子項佈局id、數據)。
  • 調用ListView的 setAdapter() 方法將適配器傳入。
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, data);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);

2. 定製界面

(1) 定義一個實體類作爲Adapter的適配類型

public class Fruit {

    private String name;

    private int imageId;

    public Fruit(String name, int imageId) {
        this.name = name;
        this.imageId = imageId;
    }

    public String getName() {
        return name;
    }

    public int getImageId() {
        return imageId;
    }

}

(2) 爲ListView子項建立自定義佈局

<!-- fruit_item.xml -->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/fruit_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/fruit_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_verticlal"
        android:layout_marginLeft="10dp" />

</LinearLayout>

(3) 創建自定義適配器

重寫構造函數和 getView() 方法,getView()會在每個子項滾動到屏幕內時被調用。

public class FruitAdapter extends ArrayAdapter<Fruit> {

    private int resourceId;

    // 重寫構造函數
    public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
        super(context, textViewResourceId, objects);
        resourceId = textViewResourceId;
    }

    // 重寫getView()方法
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    Fruit fruit = getItem(position); // 獲取當前項的Fruit實例
    View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false); // 第三個參數表示只讓父佈局的Layout屬性生效,但不爲這個View添加父佈局
    ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
    TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
    fruitImage.setImageResource(fruit.getImageId());
    fruitName.setText(fruit.getName());
    return view;
}

(4) 在MainActivity中創建Adapter對象並傳遞給ListView

public class MainActivity extends AppCompatActivity {

    private List<Fruit> fruitList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    ...
    initFruit(); // 初始化水果數據的方法
    FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
    ListView listView = (ListView) findViewById(R.id.list_view);
    listView.setAdapter(adapter);
    }

    private void initFruits() {
    ...
    Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
    fruitList.add(apple);
    ...
    }

}

3. 優化

  • 解決重複加載佈局:getView() 中convertView參數會將之前加載好的佈局進行緩存,便於之後重用。
  • 解決重複findViewById獲取控件實例:新增內部類ViewHolder對控件實例進行緩存。
public View getView(int position, View convertView, ViewGroup parent) {
    Fruit fruit = getItem(position);
    View view;
    ViewHolder viewHolder;
    if (convertView == null) {
        view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        viewHolder = new ViewHolder;
        viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
        viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
        view.setTag(viewHolder); // 將ViewHolder存儲在View中else {
    view = convertView;
    viewHolder = (ViewHolder) view.getTag(); // 重新獲取ViewHolder
    }
    viewHolder.fruitImage.setImageResource(fruit.getImageId());
    viewHolder.fruitName.setText(fruit.getName());
    return view;
}

class ViewHolder {

    ImageView fruitImage;

    TextView fruitName;

}   

4. 點擊事件

使用 setOnItemClickListener() 方法註冊監聽器,點擊子項時回調 onItemClick() 方法,重寫此方法加入點擊事件的處理邏輯。

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(adapterView<?> parent, View view, int position, long id) {
        Fruit fruit =fruitList.get(position);
        Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
    }
});

(五) RecyclerView

1. 基本用法

  • 新建適配器FruitAdapter繼承自RecyclerView.Adapter,並將泛型指定爲FruitAdapter.ViewHolder。
    • 定義一個內部類ViewHolder繼承自Recycler.ViewHolder,構造函數傳入一個View參數,通過findViewById獲取佈局中控件實例;
    • 適配器的構造函數用於將數據數據源傳進來;
    • 重寫 onCreateViewHolder()onBindViewHolder()getItemCount()這三個方法;
  • MainActivity中創建LinearLayoutManager對象,並調用 setLayoutMangager() 設置到RecyclerView中,再創建適配器實例並調用 setAdapter() 完成適配器設置。
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {

    private List<Fruit> mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder {
        ImageView fruitImage;
        TextView fruitName;
    }

    public ViewHolder(View view) {
        super(view);
        fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
        fruitName = (TextView) view.findViewById(R.id.fruit_name);
    }

    public FruitAdapter(List<Fruit> fruitList) {
        mFruitList = fruitList;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int ViewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

    @Override
    public int getItemCount() {
        return mFruitList.size();
    }

}
public class MainActivity extends AppCompatActivity {

    private List<Fruit> fruitList = new ArrayList<>();

    @Override
    protect void onCreate(Bundle savedInstanceState) {
        ...
        initFruits(); // 初始化水果數據
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager = new LinearManager(this);
        recyclerView.setLayoutManager(layoutManager);
        FruitAdapter adapter = new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);   
    }

    private void initFruits(){
        // 將水果數據添加到fruitList中
    }
}

2. 橫向滾動、網格佈局和瀑布流佈局

(1) 橫向滾動

  • 要實現橫向滾動,需把fruit_item裏的元素改爲垂直排列。
  • 調用LinearLayoutManager的 setOrientation() 來設置佈局排列方向。
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

(2) 網格佈局

  • 使用GridLayoutManager,構造函數接收兩個參數:(Context, 列數) 。
GridLayoutManager layoutManager = new GridLayoutManager(this, 2); // 兩列的網格佈局
recyclerView.setLayoutManager(layoutManager);

(3) 瀑布流佈局

  • 使用StaggeredGridLayoutManager,構造函數接收兩個參數:(列數, 排列方向) 。
StaggeredGridLayoutManager layoutManager = new StaggeredLayoutManager(3, StaggeredGridLayoutManager.VERTICAl); // 三列、縱向排列
recyclerView.setLayoutManager(layoutManager);

3. 點擊事件

需要在 onCreateViewHolder() 中自己給子項具體的View去註冊點擊事件。

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHoder> {

    ...

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int ViewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);
        final ViewHolder holder = new ViewHolder(view);
        holder.fruitImage.setOnclickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(), "點擊了圖片" + fruit.getName(), Toast.LENGTH_SHORT).show();
            }
        });
        return holder;
    }

    ...

}

二、 Fragment

(一) 碎片的使用

1. 基本用法

  • 創建碎片的佈局文件;
  • 新建類繼承自Fragment,重寫 onCreateView() 方法;
  • 在主活動的佈局文件中添加 <fragment> 標籤,需要通過 android:name 指明添加的碎片完整類名(帶包名)。
public class MyFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflate inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.my_fragment, container, false);
        return view;
    }

}
<fragment
    android:id="@+id/my_fragment"
    android:name="com.example.fragmenttest.MyFragment"
    android:layoutwidth="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1" />

2. 動態添加碎片

(1) 創建待添加碎片實例;
(2) 調用getSupportFragmentManager() 方法獲取FragmentManager;
(3) 調用 beginTransaction() 開啓一個事務;
(4) 調用 replace() 方法向容器內添加或替換碎片,需傳入 (容器id, 碎片實例);
(5) 調用 commit() 方法提交事務。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                replaceFragment(new AnotherFragment()); // 替換爲另一個碎片
            }
        });
        replaceFragment(new MyFragment()); // 初始設置爲MyFragment
    }

    private void repalceFragment(Fragment fragment) {
        FragmentManager fragmentmanamger = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.repalce(R.id.right_layout, fragment);
        transaction.commit();
    }
}

3. 碎片中模擬返回棧

調用 addToBackStack() 方法將事務添加到返回棧中,它接收一個名字用於描述返回棧狀態,一般傳入null即可,這樣按下Back鍵會回到上一個Fragment界面。

FragmentManager fragMentmanager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.addToBackStack(null); // 添加到返回棧
transaction.commit();

4. 碎片和活動間進行通信

  • 活動中調用碎片裏的方法:使用FragmentManager的 findFragmentById 獲取碎片實例,即可調用其中方法。
  • 碎片中調用活動裏的方法:使用 getActivity() 得到和當前碎片關聯的活動實例,即可調用其中方法。
MyFragment myFragment = (MyFragment) getFragmentManager().findFragmentById(R.id.my_fragment); // 活動中獲取碎片實例
MainActivity activity = (MainActivity) getActivity(); // 碎片中獲取活動實例

(二) 碎片的生命週期

與活動生命週期類似,並提供了一些的附加的回調方法:

  • onAttach():當碎片和活動建立關聯時調用。
  • onCreateView():爲碎片創建視圖(加載佈局)時調用。
  • onActivityCreated():確保與碎片關聯的活動一定已創建完畢時調用。
  • onDestroyView():當與碎片關聯的視圖被移除時調用。
  • onDetach():當碎片和活動解除關聯時調用。

碎片的生命週期

碎片中也可以通過 onSaveInstanceState() 方法保存數據,保存的數據在 onCreate()onCreateView()onActivityCreated() 中都可以得到。

(三) 動態加載佈局技巧

1. 使用限定符

  • 屏幕大小:small、normal、large、xlarge
  • 屏幕分辨率:ldpi、mdpi、hdpi、xhdpi、xxhdpi
  • 方向:land、port

2. 使用最小寬度限定符(Smallest-width Qualifier)

res目錄下新建layout-sw600dp文件夾,在其中新建acticity_main.xml佈局。

  • 屏幕寬度大於600dp的設備:加載layout-sw600dp/activity_main佈局
  • 屏幕寬度小於600dp的設備:仍會加載默認的layout/activity_main佈局。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章