博主自己开发的小项目的界面一,过程中参考了许多其他优秀博主的博文。但时间间隔较长已找不到出处,如有需要请联系博主注明原文出处!谢谢!
先上最终效果图。代码已上传至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分支