博主自己開發的小項目的界面一,過程中參考了許多其他優秀博主的博文。但時間間隔較長已找不到出處,如有需要請聯繫博主註明原文出處!謝謝!
先上最終效果圖。代碼已上傳至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分支