banner、tablayout、bottomnavigation、viewpager、fragment寫安卓界面(可按模塊看)

博主自己開發的小項目的界面一,過程中參考了許多其他優秀博主的博文。但時間間隔較長已找不到出處,如有需要請聯繫博主註明原文出處!謝謝!

先上最終效果圖。代碼已上傳至github,鏈接見文末。
頂部爲輪播圖banner空間,放入搜索框(尚未實現具體的搜索功能)。中間爲textview組件。“兼職工作”與“短期實習”爲tablayout組件,下列表格爲recyclerview可拖動。底部爲bottomnavigation組件。tablayout組件點擊不同標題會呈現不同的列表內容,bottomnavigation尚未實現轉換,留待整個項目結束後進行設置(僅有樣式)。
圖標來源於阿里iconfont
界面一


寫在前面

添加的所有依賴如下
module文件

	implementation 'com.youth.banner:banner:1.4.10'
    implementation 'com.github.bumptech.glide:glide:4.11.0'
    //noinspection GradleCompatible
    implementation 'com.android.support:design:28.0.0'
    //noinspection GradleCompatible
    implementation 'com.android.support:recyclerview-v7:28.0.0'
    implementation 'com.github.bumptech.glide:glide:4.11.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
    implementation 'com.ashokvarma.android:bottom-navigation-bar:2.2.0'

project文件

allprojects {
    repositories {
        google()
        jcenter()
        maven{url 'http://jitpack.io'}
    }
}

設置manifest主題爲無主題

android:theme="@style/Theme.AppCompat.Light.NoActionBar">

總注1:最外層佈局格式爲流式佈局
總注2:真機實驗型號爲HuaweiMate10pro
總注3:代碼先後順序總爲先xml後java
總注4:全局變量與部分初始化展示
總注5:具體的命名的顏色不在博客中展示

    private Banner mbanner;
    private SearchView mSearchView;
    private MyImageLoader myImageLoader;
    private ArrayList<Integer> imagePath;
    private ArrayList<String> imageTitle;
    private TextView textView;

    private TabLayout mytab;
    private ViewPager myViewPager;
    private List<String> mtitle = new ArrayList<>();
    private List<Fragment> mFragments = new ArrayList<>();

 private void initData() {
        imagePath = new ArrayList<>();
        imageTitle = new ArrayList<>();
        for (int i=0;i<5;i++){
            imagePath.add(R.mipmap.ic_launcher);//圖片(示例圖片)
            imageTitle.add("ok"+i);//圖片名
        }
    }

banner輪播圖與搜索框

   <com.youth.banner.Banner
        android:id="@+id/banner"
        android:layout_width="match_parent"
        android:layout_height="175dp">

        <androidx.appcompat.widget.SearchView
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:layout_marginTop="20dp"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="80dp"
            android:background="@drawable/searchview"
            app:queryHint="軟件開發" />

    </com.youth.banner.Banner>

注1:queryHint屬性:點擊搜索框後默認出現的提示字

 private void initView() {
        myImageLoader = new MyImageLoader();
        mbanner = findViewById(R.id.banner);
        mbanner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE);
        //設置圓形指示器和標題
        mbanner.setImageLoader(myImageLoader);
        //設置圖片加載器
        mbanner.setBannerAnimation(Transformer.ZoomOutSlide);
        //圖片輪播效果(動畫常量類)
        mbanner.setBannerTitles(imageTitle);
        //設置圖片名(效果圖中左下角)
        mbanner.setDelayTime(2000);
        //延遲時間
        mbanner.isAutoPlay(true);
        //是否自動播放
        mbanner.setIndicatorGravity(BannerConfig.CENTER);
        //設置指示器的位置
        mbanner.setImages(imagePath).setOnBannerListener(new OnBannerListener() {
            @Override
            public void OnBannerClick(int position) {
               switch (position){
                   case 0:
                       break;
                   case 1:
                       break;
                   case 2:
                       break;
                   case 3:
                       break;
                   case 4:
                       break;
               }
            }
        }).start();
		//根據圖片標號指定動作,示例中沒有增加任何動作
    }

public class MyImageLoader extends ImageLoader{

        @Override
        public void displayImage(Context context, Object path, ImageView imageView) {
            Glide.with(context.getApplicationContext()).load(path).into(imageView);
        }
    }

注1:來源於github上的開源項目banner,具體參數可見github上的說明。鏈接如下:banner的使用及源碼
注2:在主方法中調用initView()initData()即可


中間的幾個textview(只展示xml)

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/bulletin"
            android:layout_width="100dp"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="快訊"
            android:textSize="30sp" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="10dp"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:gravity="left|center"
                android:text="大三學生經濟獨立不是夢"
                android:textSize="15sp" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:gravity="left|center"
                android:text="互聯網企業進入“新寒冬”"
                android:textSize="15sp" />
        </LinearLayout>

    </LinearLayout>

Tablayout與ViewPager

 <com.google.android.material.tabs.TabLayout
            android:id="@+id/MyTab"
            android:layout_width="match_parent"
            android:layout_height="45dp">

 		</com.google.android.material.tabs.TabLayout>
 <androidx.viewpager.widget.ViewPager
            android:id="@+id/MyViewPager"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">

        </androidx.viewpager.widget.ViewPager>

這是整個界面中比較複雜的部分。因此將全部的代碼都貼上來並詳細解釋。代碼中可能會包括之前提到的部分與後面的bottomnavigation部分。
佈局文件中有weight=1,是與後面的bottomnavigation組件實現自動填充。即在同一個流式佈局中,上下兩個固定,中間填滿則是weight=1
下面這段代碼是MainActivity中的onCreate部分。可以看到在mFragments中添加了自定義的CollectFragment類,這個自定義的類實際上是實現了具體列表內容的recyclerview。把mFragment想象成一個盒子,CollectFragemtn就是自己定義的類型把它裝滿,而裝滿後的樣子是一個recyclerview列表。

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();//剛纔的初始化工作

        mytab = findViewById(R.id.MyTab);
        myViewPager = findViewById(R.id.MyViewPager);//找到對應的組件

        mtitle.add("兼職工作");
        mtitle.add("短期實習");//tablayout中的標題

        mFragments.add(new CollectFragment());
        mFragments.add(new CollectFragment());//在fragment中添加界面

		//爲viewPager設置適配器
        myViewPager.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager()) {
            @NonNull
            @Override
            public Fragment getItem(int position) {
                return mFragments.get(position);
            }

            @Override
            public int getCount() {
                return mFragments.size();//顯示多少個界面
            }

            @Override
            public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
                //super.destroyItem(container, position, object);
            }

            @Nullable
            @Override
            public CharSequence getPageTitle(int position) {
                return mtitle.get(position);
            }
        });

        myViewPager.setOffscreenPageLimit(2);//設置預加載界面的數量
        mytab.setupWithViewPager(myViewPager);//設置tablayout界面下的viewpager

    }

先做一些其他準備,定義示例商品的類。不要因爲長就放棄!都是可以自動生成的get和set方法以及構造方法。

public class GoodsEntity implements Serializable {
    private String imgPath;
    private String goodsName;
    private String goodsPrice;
    private String goodsRequire;
    private String imgCompany;
    private String Companymes;

    public GoodsEntity(){}

    public String toString(){
        return "GoodsEntity{"+
                "imgPath='"+imgPath+'\''+
                ",goodsName='"+goodsName+'\''+
                ",goodsPrice='"+goodsPrice+'\''+
                '}';
    }

    public String getImgPath() {
        return imgPath;
    }

    public void setImgPath(String imgPath) {
        this.imgPath = imgPath;
    }

    public String getGoodsName() {
        return goodsName;
    }

    public void setGoodsName(String goodsName) {
        this.goodsName = goodsName;
    }

    public String getGoodsPrice() {
        return goodsPrice;
    }

    public void setGoodsPrice(String goodsPrice) {
        this.goodsPrice = goodsPrice;
    }

    public String getGoodsRequire() {
        return goodsRequire;
    }

    public void setGoodsRequire(String goodsRequire) {
        this.goodsRequire = goodsRequire;
    }

    public String getImgCompany() {
        return imgCompany;
    }

    public void setImgCompany(String imgCompany) {
        this.imgCompany = imgCompany;
    }

    public String getCompanymes() {
        return Companymes;
    }

    public void setCompanymes(String companymes) {
        Companymes = companymes;
    }

    public GoodsEntity(String imgPath, String goodsName, String goodsPrice, String goodsRequire, String imgCompany, String companymes){
        this.imgPath = imgPath;
        this.goodsName = goodsName;
        this.goodsPrice = goodsPrice;
        this.goodsRequire = goodsRequire;
        this.imgCompany = imgCompany;
        this.Companymes = companymes;
    }

}

接下來是recyclerview的適配器。這其中的R.layout.items是具體內容的佈局,可以自己編寫xml文件,在此不作展示。

public class CollectRecyclerAdapter extends RecyclerView.Adapter<CollectRecyclerAdapter.myViewHolder> {
    private Context context;
    private ArrayList<GoodsEntity> goodsEntityList;

    //創建構造函數
    public CollectRecyclerAdapter(Context context, ArrayList<GoodsEntity> goodsEntityList) {
        //將傳遞過來的數據,賦值給本地變量
        this.context = context;//上下文環境
        this.goodsEntityList = goodsEntityList;//實體類數據
    }


    //用於創建ViewHolder實例
    public myViewHolder onCreateViewHolder(ViewGroup parent,int viewType){
        //創建自定義佈局   加載佈局
        View itemView = View.inflate(context,R.layout.items,null);
        return new myViewHolder(itemView);
    }

    public void onBindViewHolder(myViewHolder holder,int position){
        //根據點擊位置綁定數據
        //用於對RecyclerView子項的數據進行賦值,在每個子項被滾動到屏幕內的時候執行
        GoodsEntity data = goodsEntityList.get(position);
        holder.mitemgoodsname.setText(data.getGoodsName());//獲取實體類中的name字段並設置
        holder.mitemgodosprice.setText(data.getGoodsPrice());//獲取實體類中的price字段並設置
    }

    public int getItemCount(){
        return goodsEntityList.size();
    }

    class myViewHolder extends RecyclerView.ViewHolder{
        private ImageView mitemgoodsimg;
        private TextView mitemgoodsname;
        private TextView mitemgodosprice;

        public myViewHolder(View itemView){
            super(itemView);
            mitemgoodsimg = itemView.findViewById(R.id.item_goods_img);
            mitemgoodsname = itemView.findViewById(R.id.item_goods_name);
            mitemgodosprice = itemView.findViewById(R.id.item_goods_price);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(context,"點擊了",Toast.LENGTH_SHORT).show();
                    //回傳點擊監聽事件
                    if(onItemClickListener!=null){
                        onItemClickListener.OnItemClick(v,goodsEntityList.get(getLayoutPosition()));
                    }
                }
            });
        }

    }
    /*
     * 設置item的監聽事件的接口
     * */
    public interface OnItemClickListener{
        /*
         * @param view 點擊的item的試圖
         * @param data 點擊的item的數據
         * */
        public void OnItemClick(View view, GoodsEntity data);
    }

    //需要外部訪問設置set方法方便調用
    private OnItemClickListener onItemClickListener;
    public void setOnItemClickListener(OnItemClickListener onItemClickListener){
        this.onItemClickListener = onItemClickListener;
    }
}

最後一步是實現將前文提到的CollectFragment。這裏的xml文件(R.id.collect_recyclerView)佈局定義的是具體信息之間的佈局格式。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/collect_recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>
public class CollectFragment extends Fragment {

    private View view;
    public RecyclerView mCollectRecyclerView;
    private ArrayList<GoodsEntity> goodsEntities = new ArrayList<>();
    private CollectRecyclerAdapter mCollectRecyclerAdapter;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        //獲取fragment的layout
        view = inflater.inflate(R.layout.collect_page,container, false);
        //對recycleview進行配置
        initRecyclerView();
        //模擬數據
        initData();
        return view;
    }

    private void initData(){
        for(int i=0;i<10;i++){
            GoodsEntity goodsEntity = new GoodsEntity();
            goodsEntity.setGoodsName("軟件工程"+i);
            goodsEntity.setGoodsPrice("100"+i*1000);
            goodsEntities.add(goodsEntity);
        }
    }

    private void initRecyclerView() {
        //獲取recyclerview
        mCollectRecyclerView=(RecyclerView)view.findViewById(R.id.collect_recyclerView);
        //創建adapter
        mCollectRecyclerAdapter = new CollectRecyclerAdapter(getActivity(), goodsEntities);
        //給recyclerview設置adapter
        mCollectRecyclerView.setAdapter(mCollectRecyclerAdapter);
        //設置layoutmanager,可以設置顯示效果是線性佈局、grid佈局還是瀑布流
        //參數是:上下文、列表方向、是否倒敘
        mCollectRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
        //設置item的分割線
        mCollectRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
        //recyclerview沒有item的監聽事件,需要自己在適配器中寫一個監聽事件
        mCollectRecyclerAdapter.setOnItemClickListener(new CollectRecyclerAdapter.OnItemClickListener() {
            @Override
            public void OnItemClick(View view, GoodsEntity data) {
                Toast.makeText(getActivity(),"item", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

至此,中間部分就完成了。總結一下:ViewPager中包含了是最外面的盒子,裏面包含了一些碎片fragments,用自定義的CollectFragment進行填充,由適配器決定信息與內容間的格式,xml文件決定內容內部的更具體的文字圖片信息格式。


bottomnavigation

在安卓新建項目的時候有自帶的bottomnavigation,因此在它的基礎上稍微做了一點改動。

<com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/nav_view"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:background="#fff"
            app:itemTextColor="@color/navigation_item_color"
            app:itemIconTint="@color/navigation_item_color"
            app:labelVisibilityMode="labeled"
            app:menu="@menu/bottom_nav_menu" />

踩過的坑:
1.使用選擇器來指定按鈕的顏色,根據不同的狀態顯示不同的顏色。新建一個color文件夾存儲各種狀態下的不同顏色。代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/colorBlack" android:state_checked="false"/>
    <item android:color="@color/colorMyBlue" android:state_checked="true"/>
</selector>

2.menu菜單決定下面的圖標個數及格式。注意圖標的個數應限制在3-5個。(具體原因及可能出現的問題未知,日後補坑)代碼如下:
還要注意的地方是圖片格式。如果圖片格式是PNG則選擇器將起到作用。而圖片是SVG則無法改變其顏色(實踐結果未看官方文檔)

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:enabled="true"
        android:id="@+id/navigation_home"
        android:icon="@mipmap/home"
        app:showAsAction="ifRoom"
        android:title="首頁"/>

    <item
        android:enabled="true"
        android:id="@+id/navigation_launch"
        android:icon="@mipmap/add"
        app:showAsAction="ifRoom"
        android:title="發佈"/>

    <item
        android:enabled="true"
        android:id="@+id/navigation_message"
        android:icon="@mipmap/message"
        app:showAsAction="ifRoom"
        android:title="消息"/>

    <item
        android:enabled="true"
        android:id="@+id/my"
        android:icon="@mipmap/mine"
        app:showAsAction="ifRoom"
        android:title="我的"/>
</menu>

如果需要對bottomnavigation進行動作監聽設置的話可以通過id進行連接。具體設置留坑以後補。
至此頁面上的所有組件均已完成。
所有代碼上傳至github ptjob分支

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章