当前位置: 首页 > news >正文

Android TabLayout 使用进阶(含源码)

android:layout_height=“match_parent”

android:orientation=“vertical”

tools:context=“.mode2.ClassificationActivity”>

<com.google.android.material.tabs.TabLayout

android:id=“@+id/tab_layout”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:background=“#FFF”

app:tabIndicatorColor=“#00FF00”

app:tabIndicatorFullWidth=“false”

app:tabMode=“scrollable”

app:tabRippleColor=“#00000000”

app:tabSelectedTextColor=“#00FF00”

app:tabTextColor=“#000” />

<androidx.viewpager.widget.ViewPager

android:id=“@+id/view_pager”

android:layout_width=“match_parent”

android:layout_height=“match_parent”/>

还差Fragment了,假设当前的Activity是要做视频的分类,有类别如下:电视剧、电影、综艺、体育、新闻、国际这六项。那么我们就需要建6个Fragment,这个些fragment同样放在mode2包下。分别是

① 创建Fragment

TVSeriesFragment

public class TVSeriesFragment extends Fragment {

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_tv_series,

container, false);

return view;

}

}

MovieFragment

public class MovieFragment extends Fragment {

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_movie,

container, false);

return view;

}

}

VarietyShowFragment

public class VarietyShowFragment extends Fragment {

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_variety_show,

container, false);

return view;

}

}

SportsFragment

public class SportsFragment extends Fragment {

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_sports,

container, false);

return view;

}

}

NewsFragment

public class NewsFragment extends Fragment {

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_news,

container, false);

return view;

}

}

InternationalFragment。

public class InternationalFragment extends Fragment {

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_international,

container, false);

return view;

}

}

六个Fragment各自对应的xml如下:

fragment_tv_series.xml

<?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:gravity=“center”

android:orientation=“vertical”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“电视剧”

android:textColor=“#000”

android:textSize=“24sp” />

fragment_movie.xml

<?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:gravity=“center”

android:orientation=“vertical”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“电影”

android:textColor=“#000”

android:textSize=“24sp” />

fragment_variety_show.xml

<?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:gravity=“center”

android:orientation=“vertical”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“综艺”

android:textColor=“#000”

android:textSize=“24sp” />

fragment_sports.xml

<?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:gravity=“center”

android:orientation=“vertical”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“体育”

android:textColor=“#000”

android:textSize=“24sp” />

fragment_news.xml

<?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:gravity=“center”

android:orientation=“vertical”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“新闻”

android:textColor=“#000”

android:textSize=“24sp” />

fragment_international.xml

<?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:gravity=“center”

android:orientation=“vertical”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“国际”

android:textColor=“#000”

android:textSize=“24sp” />

我这么实在的博主现在可不多了,这么多余的代码我都给贴出来了。

② Fragment适配器

现在Fragment的就写好了。下面写一个适配器,在com.llw.tablayoutdemo下新建一个adapter包,该包下新建一个BasicFragmentAdapter,里面的代码如下:

package com.llw.tablayoutdemo.adapter;

import android.view.ViewGroup;

import androidx.annotation.NonNull;

import androidx.annotation.Nullable;

import androidx.fragment.app.Fragment;

import androidx.fragment.app.FragmentManager;

import androidx.fragment.app.FragmentPagerAdapter;

import java.util.List;

/**

  • Fragment适配器

  • @author llw

  • @date 2021/4/28 15:08

*/

public class BasicFragmentAdapter extends FragmentPagerAdapter {

String titleArr[];

List mFragmentList;

public BasicFragmentAdapter(FragmentManager fm, List list, String[] titleArr) {

super(fm);

mFragmentList = list;

this.titleArr = titleArr;

}

@Override

public Fragment getItem(int i) {

return mFragmentList.get(i);

}

@Override

public int getCount() {

return mFragmentList != null ? mFragmentList.size() : 0;

}

@Nullable

@Override

public CharSequence getPageTitle(int position) {

return titleArr[position];

}

@Override

public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {

// super.destroyItem(container, position, object);

}

}

③ 编码运行

现在都具备了,回到ClassificationActivity中,修改代码如下:

package com.llw.tablayoutdemo.mode2;

import androidx.appcompat.app.AppCompatActivity;

import androidx.fragment.app.Fragment;

import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;

import com.google.android.material.tabs.TabLayout;

import com.llw.tablayoutdemo.R;

import com.llw.tablayoutdemo.adapter.BasicFragmentAdapter;

import java.util.ArrayList;

import java.util.List;

/**

  • 分类页面 ;TabLayout + ViewPager + Fragment

  • @author llw

*/

public class ClassificationActivity extends AppCompatActivity {

private TabLayout tabLayout;

private ViewPager viewPager;

String[] titleArray = new String[]{“电视剧”, “电影”, “综艺”, “体育”, “新闻”, “国际”};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_classification);

tabLayout = findViewById(R.id.tab_layout);

viewPager = findViewById(R.id.view_pager);

List fragmentList = new ArrayList<>();

fragmentList.add(new TVSeriesFragment());

fragmentList.add(new MovieFragment());

fragmentList.add(new VarietyShowFragment());

fragmentList.add(new SportsFragment());

fragmentList.add(new NewsFragment());

fragmentList.add(new InternationalFragment());

BasicFragmentAdapter adapter = new BasicFragmentAdapter(getSupportFragmentManager(), fragmentList, titleArray);

viewPager.setAdapter(adapter);

tabLayout.setupWithViewPager(viewPager);

}

}

可以看到代码不多,下面运行一下吧。对了,还缺少一个页面入口,修改activity_main.xml,在里面增加一个按钮。

<Button

android:text=“分类页面:TabLayout + ViewPager + Fragment”

android:onClick=“mode2”

android:textAllCaps=“false”

android:layout_width=“match_parent”

android:layout_height=“50dp”/>

然后在MainActivity中增加一个方法。

/**

  • 组合使用 分类页面 ;TabLayout + ViewPager + Fragment

  • @param view

*/

public void mode2(View view) { startActivity(new Intent(this, ClassificationActivity.class)); }

现在可以运行了。

在这里插入图片描述

不过这个文字并没有放大,那么再来设置一下,这里通过TextView来实现,在layout下新建一个tab_item.xml,里面的代码如下:

<?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=“wrap_content”

android:orientation=“vertical”>

<TextView

android:id=“@+id/tv_title”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_gravity=“center”

android:gravity=“center”

android:textColor=“#333333”

android:textSize=“14sp” />

然后在ClassificationActivity中增加如下方法。

/**

  • 初始化TabLayout

*/

private void initTabLayout() {

for (int i = 0; i < tabLayout.getTabCount(); i++) {

TabLayout.Tab tab = tabLayout.getTabAt(i);

if (tab != null) {

//设置标签视图 为 TextView

tab.setCustomView(getTabView(i));

}

}

updateTabText(tabLayout.getTabAt(tabLayout.getSelectedTabPosition()), true);

tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {

@Override

public void onTabSelected(TabLayout.Tab tab) {

updateTabText(tab,true);

}

@Override

public void onTabUnselected(TabLayout.Tab tab) {

updateTabText(tab,false);

}

@Override

public void onTabReselected(TabLayout.Tab tab) {

}

});

}

/**

  • 获取TabView

  • @param currentPosition

  • @return

*/

private View getTabView(int currentPosition) {

View view = LayoutInflater.from(this).inflate(R.layout.tab_item, null);

TextView textView = view.findViewById(R.id.tv_title);

textView.setText(titleArray[currentPosition]);

return view;

}

/**

  • 更新标签文字

  • @param tab

  • @param isSelect

*/

private void updateTabText(TabLayout.Tab tab, boolean isSelect) {

if (isSelect) {

//选中文字加大加粗

TextView tabSelect = tab.getCustomView().findViewById(R.id.tv_title);

tabSelect.setTextSize(22);

tabSelect.setTextColor(Color.parseColor(“#00FF00”));

tabSelect.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));

tabSelect.setText(tab.getText());

} else {

TextView tabUnSelect = tab.getCustomView().findViewById(R.id.tv_title);

tabUnSelect.setTextSize(14);

tabUnSelect.setTextColor(Color.BLACK);

tabUnSelect.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));

tabUnSelect.setText(tab.getText());

}

}

最后调用在onCreate方法中调用initTabLayout()方法。

在这里插入图片描述

运行一下:

在这里插入图片描述

嗯,这个效果还是阔以滴。

三、App主页面 (TabLayout + TabItem + ViewPager + Fragment)


现在常规的App主页面都是底部有几个菜单,4个或者5个。通讯类的基本上是4个,如果QQ、微信。购物类的基本上是5个,如果淘宝、天猫、京东等。至于有几个我们不管,主要是怎么去实现这个主页面的菜单切换。这里的实现方式其实有很多,而文本以TabLayout为主,那么自然是以TabLayout来现实了,就如我标题上说的一样,用到了,TabLayout + TabItem + ViewPager + Fragment。

① 选中图标

下面就来看看具体怎么去做吧。

这里是第三个使用方式了,那么在com.llw.tablayoutdemo包下新建一个mode3包,这个包下新建一个HomeActivity,用来作为App的主页面,然后它的布局activity_home.xml是有一点点麻烦的。里面会用到8个图标。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

将图标放到layout下的drawable文件夹中。

然后在这个文件夹下新建四个xml文件,分别是:

app_home.xml

<?xml version="1.0" encoding="utf-8"?>

app_search.xml

<?xml version="1.0" encoding="utf-8"?>

app_find.xml

<?xml version="1.0" encoding="utf-8"?>

app_mine.xml

<?xml version="1.0" encoding="utf-8"?>

下面来写activity_home.xml,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

tools:context=“.mode3.HomeActivity”>

<androidx.viewpager.widget.ViewPager

android:id=“@+id/view_pager”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:layout_above=“@+id/tab_layout” />

<com.google.android.material.tabs.TabLayout

android:id=“@+id/tab_layout”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_alignParentBottom=“true”

android:background=“#FFF”

android:minHeight=“?attr/actionBarSize”

app:tabIndicatorColor=“#00000000”

app:tabRippleColor=“#00000000”

app:tabSelectedTextColor=“#1296DB”

app:tabTextColor=“#929299”>

<com.google.android.material.tabs.TabItem

android:id=“@+id/item_home”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:icon=“@drawable/app_home”

android:text=“主页” />

<com.google.android.material.tabs.TabItem

android:id=“@+id/item_search”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:icon=“@drawable/app_search”

android:text=“搜索” />

<com.google.android.material.tabs.TabItem

android:id=“@+id/item_find”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:icon=“@drawable/app_find”

android:text=“发现” />

<com.google.android.material.tabs.TabItem

android:id=“@+id/item_mine”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:icon=“@drawable/app_mine”

android:text=“我的” />

</com.google.android.material.tabs.TabLayout>

这里对TabLayout控件做了一些修改,设置点击的水波纹为透明、下划线为透明,选中的文字颜色为蓝色,默认是灰色,和刚才创建的四个图标样式文件类似,选中时切换蓝色图片,未选中时灰色图片。

② 创建Fragment

这里tabItem就是用来控制菜单图片的。现在布局已经写好了,下面来写代码。

我们的主页面自然也需要显示多个Fragment,通过ViewPager来进行切换。

而这些Fragment都放在mode3包下,下面一个一个的来创建

HomeFragment

public class HomeFragment extends Fragment {

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_home,

container, false);

return view;

}

}

fragment_home.xml

<?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:gravity=“center”

android:orientation=“vertical”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“Home”

android:textColor=“#000”

android:textSize=“24sp” />

SearchFragment

public class SearchFragment extends Fragment {

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_search,

container, false);

return view;

}

}

fragment_search.xml

<?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:gravity=“center”

android:orientation=“vertical”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“Search”

android:textColor=“#000”

android:textSize=“24sp” />

FindFragment

public class FindFragment extends Fragment {

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_find,

container, false);

return view;

}

}

fragment_find.xml

<?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:gravity=“center”

android:orientation=“vertical”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“Find”

android:textColor=“#000”

android:textSize=“24sp” />

MineFragment

public class MineFragment extends Fragment {

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_mine,

container, false);

return view;

}

}

fragment_mine.xml

<?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:gravity=“center”

android:orientation=“vertical”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“Mine”

android:textColor=“#000”

android:textSize=“24sp” />

③ 编码运行

现在四个Fragment和它们的布局都写好了,下面回到HomeActivity中,修改代码如下:

package com.llw.tablayoutdemo.mode3;

import androidx.appcompat.app.AppCompatActivity;

import androidx.fragment.app.Fragment;

import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;

import com.google.android.material.tabs.TabLayout;

import com.llw.tablayoutdemo.R;

import com.llw.tablayoutdemo.adapter.BasicFragmentAdapter;

import java.util.ArrayList;

import java.util.List;

/**

  • 组合使用 主页面 ;TabLayout + TabItem + ViewPager + Fragment

  • @author llw

*/

public class HomeActivity extends AppCompatActivity {

private TabLayout tabLayout;

private ViewPager viewPager;

final String[] titleArray = new String[]{“首页”, “搜索”, “发现”, “我的”};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_home);

init();

}

private void init() {

tabLayout = findViewById(R.id.tab_layout);

viewPager = findViewById(R.id.view_pager);

List fragmentList = new ArrayList<>();

fragmentList.add(new HomeFragment());

fragmentList.add(new SearchFragment());

fragmentList.add(new FindFragment());

fragmentList.add(new MineFragment());

BasicFragmentAdapter adapter = new BasicFragmentAdapter(getSupportFragmentManager(), fragmentList, titleArray);

viewPager.setAdapter(adapter);

tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {

@Override

public void onTabSelected(TabLayout.Tab tab) {

viewPager.setCurrentItem(tab.getPosition());

}

@Override

public void onTabUnselected(TabLayout.Tab tab) {

}

@Override

public void onTabReselected(TabLayout.Tab tab) {

}

});

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

@Override

public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

tabLayout.setScrollPosition(position,positionOffset,true);

}

@Override

public void onPageSelected(int position) {

}

@Override

public void onPageScrollStateChanged(int state) {

}

});

}

}

那么这里就是切换Tab的时候改变ViewPager,ViewPager改变的时候切换Tab选中。现在还不能运行的,因为缺少一个入口,这个入口依然在MainActivity中,

在activity_main.xml中添加一个按钮:

<Button

android:text=“主页面:TabLayout + TabItem + ViewPager + Fragment”

android:onClick=“mode3”

android:textAllCaps=“false”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”/>

在MainActivity中添加一个方法:

/**

  • 组合使用 主页面 ;TabLayout + TabItem + ViewPager + Fragment

  • @param view

*/

public void mode3(View view) {

startActivity(new Intent(this, HomeActivity.class));

}

下面运行一下:

在这里插入图片描述

可以看到我点击TabLayout,ViewPager就会切换,滑动ViewPager,TabLayout就会选中相应的TabItem。

这样就类似于现在的App主页面了。

四、商品分类页面


什么是商品分类页面呢?如下图

在这里插入图片描述

就像这种页面,你在日常的使用中应该见过。通常是在购物APP里面居多。但这个也是一个使用场景之一。那么这个页面要怎么做呢?

我们来分析一下啊,首先左边不出意外是一个列表,它的表现形式可以有多种,你可以使用RecyclerView,也可以使用TabLayout,毫无疑问我要使用TabLayout,而右边的就是一个ViewPager,可以上下滑动切换的ViewPager,里面放Fragment或者RecyclerView。我目前先这么自以为是的猜测一下。

那么下面就来实践一下吧。

① 添加第三方依赖库

首先在app下的build.gradle的dependencies{}闭包中添加如下依赖:

//纵向TabLayout

implementation ‘q.rorbin:VerticalTabLayout:1.2.5’

//可以纵向滑动的ViewPager

implementation ‘cn.youngkaaa:yviewpager:0.4’

然后Sync Now。

② 创建页面

那么现在是使用的第四个方式了,在com.llw.tablayoutdemo包下新建mode4包,这个包下新建一个GoodsActivity,布局activity_goods.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

tools:context=“.mode4.GoodsActivity”>

<q.rorbin.verticaltablayout.VerticalTabLayout

android:id=“@+id/tab_layout”

android:layout_width=“80dp”

android:layout_height=“match_parent”

android:background=“#EDEDED”

app:indicator_color=“#FFFFFF”

app:indicator_gravity=“fill”

app:tab_height=“50dp”

app:tab_mode=“scrollable” />

<cn.youngkaaa.yviewpager.YViewPager

android:id=“@+id/view_pager”

android:background=“#FFF”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:layout_toEndOf=“@id/tab_layout”

app:orientation=“vertical”/>

到这里我们思考一个问题,假设不知道商品的类别数量的情况下,怎么写,那么肯定不能写死这个Fragment,不能像之前一样创建,那样明显太笨重了不是吗?像这种商品分类页面里面的布局都是一样的,不同的只是数据而已,而这个数据也是可以变化的,因此你不能写死数据和Fragment,因此就需要动态来生成。下面在mode4包下新建一个GoodsTabFragment,它的布局是fragment_goods_tab.xml,布局代码如下:

<?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:gravity=“center”

android:orientation=“vertical”>

<TextView

android:id=“@+id/tv_content”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“内容”

android:textColor=“#000”

android:textSize=“24sp” />

这里我只是放了一个TextView,用来显示当前是哪一个商品所对应的Fragment。而GoodsTabFragment的代码也是很简单的,如下:

public class GoodsTabFragment extends Fragment {

private TextView tvContent;

private String content;

public GoodsTabFragment(String content) {

this.content = content;

}

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_goods_tab,

container, false);

tvContent = view.findViewById(R.id.tv_content);

return view;

}

@Override

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

tvContent.setText(content);

}

}

③ 创建适配器

下面还需要一个适配器,在adapter包下新建一个GoodsFragmentAdapter,里面的代码如下:

public class GoodsFragmentAdapter extends FragmentPagerAdapter {

private ArrayList fragments;

private ArrayList tabName;

public GoodsFragmentAdapter(FragmentManager fm, ArrayList fragments, ArrayList

tabName) {

super(fm);

this.fragments = fragments;

this.tabName = tabName;

}

@Override

public Fragment getItem(int i) {

return fragments.get(i);

}

@Override

public int getCount() {

return fragments.size();

}

@Nullable

@Override

public CharSequence getPageTitle(int position) {

return tabName.get(position);

}

}

④ 编码运行

然后回到GoodsActivity中,编写代码如下:

/**

  • 商品分类页面 : VerticalTabLayout + YViewPager + Fragment

  • @author llw

*/

public class GoodsActivity extends AppCompatActivity {

private VerticalTabLayout tabLayout;

private YViewPager viewPager;

private List titleList = new ArrayList<>();

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_goods);

tabLayout = findViewById(R.id.tab_layout);

viewPager = findViewById(R.id.view_pager);

final ArrayList tabName = new ArrayList<>();

final ArrayList fragments = new ArrayList<>();

int num = new Random().nextInt(50);

Toast.makeText(this, num + “个商品”, Toast.LENGTH_SHORT).show();

for (int i = 0; i < num; i++) {

titleList.add(“商品” + i);

}

for (int i = 0; i < titleList.size(); i++) {

fragments.add(new GoodsTabFragment(titleList.get(i)));

tabName.add(titleList.get(i));

}

GoodsFragmentAdapter fragTabAdapter = new GoodsFragmentAdapter(getSupportFragmentManager(), fragments,

tabName);

viewPager.setAdapter(fragTabAdapter);

tabLayout.setupWithViewPager(viewPager);

}

}

设置一个50以内的随机数,然后设置菜单和Fragment,运行一下:

在这里插入图片描述

这还是不难的对吧。

五、个人主页面


有些App在个人的信息页面会展示较多的数据,这里利用TabLayout就可以更合理的展示,

首先准备一个头像图片

在这里插入图片描述

越前龙马。

① 新建页面

这是我们的第五个使用方式了,在com.llw.tablayoutdemo下新建一个mode5包,包下新建一个PersonActivity,布局是activity_person.xml。

下面还需要添加一个这个页面的主题风格,在styles.xml中添加如下代码:

然后在AndroidManifest.xml中使用。

在这里插入图片描述

这样设置,你刚才的风格样式就只对这个PersonActivity生效,不会影响到其他的Activity。

由于这个步骤会比较的多,所以运行的次数比较多,我们提前写入口,打开activity_main.xml,添加一个按钮。

<Button

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:onClick=“mode5”

android:text=“个人页面 : CoordinatorLayout + TabLayout + ViewPager + Fragment”

android:textAllCaps=“false” />

在MainActivity中添加如下方法:

/**

  • 组合使用 个人主页面 : CoordinatorLayout + TabLayout + ViewPager + Fragment

  • @param view

*/

public void mode5(View view) {

startActivity(new Intent(this, PersonActivity.class));

}

下面来看看这个布局文件activity_person.xml,里面的代码比较的多:

<?xml version="1.0" encoding="utf-8"?>

<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:background=“#EEEEEE”

android:fitsSystemWindows=“true”

android:orientation=“vertical”

tools:context=“.mode5.PersonActivity”>

<com.google.android.material.appbar.AppBarLayout

android:id=“@+id/appbar_layout”

android:layout_width=“match_parent”

android:layout_height=“280dp”

android:fitsSystemWindows=“true”

android:theme=“@style/ThemeOverlay.AppCompat.Dark.ActionBar”>

<com.google.android.material.appbar.CollapsingToolbarLayout

android:id=“@+id/toolbar_layout”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:fitsSystemWindows=“true”

app:collapsedTitleGravity=“center”

app:contentScrim=“#1296db”

app:layout_scrollFlags=“scroll|exitUntilCollapsed”

app:title=“初学者-Study”

app:toolbarId=“@+id/toolbar”>

<RelativeLayout

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:fitsSystemWindows=“true”

app:layout_collapseMode=“parallax”

app:layout_collapseParallaxMultiplier=“1”>

<ImageView

android:id=“@+id/iv_bg”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:scaleType=“centerCrop”

app:layout_collapseMode=“pin”

app:layout_scrollFlags=“scroll|snap” />

<View

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:layout_marginTop=“140dp”

android:background=“#FFF” />

<com.google.android.material.imageview.ShapeableImageView

android:id=“@+id/iv_user”

android:layout_width=“100dp”

android:layout_height=“100dp”

android:layout_centerInParent=“true”

android:padding=“1dp”

android:src=“@drawable/logo”

app:shapeAppearanceOverlay=“@style/circleImageStyle”

app:strokeColor=“#FFF”

app:strokeWidth=“2dp” />

<com.google.android.material.imageview.ShapeableImageView

android:id=“@+id/iv_sex_bg”

android:layout_width=“30dp”

android:layout_height=“30dp”

android:layout_alignEnd=“@+id/iv_user”

android:layout_alignBottom=“@+id/iv_user”

android:background=“#1296db”

android:padding=“1dp”

app:shapeAppearanceOverlay=“@style/circleImageStyle”

app:strokeColor=“#FFF”

app:strokeWidth=“1dp” />

<com.google.android.material.imageview.ShapeableImageView

android:layout_width=“20dp”

android:layout_height=“20dp”

android:layout_marginEnd=“5dp”

android:layout_marginBottom=“5dp”

android:layout_alignEnd=“@+id/iv_user”

android:layout_alignBottom=“@+id/iv_user”

android:src=“@drawable/icon_man” />

<TextView

android:id=“@+id/tv_nickname”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_below=“@+id/iv_user”

android:layout_centerHorizontal=“true”

android:layout_marginTop=“12dp”

android:text=“初学者-Study”

android:textColor=“#000”

android:textSize=“20sp” />

<TextView

android:id=“@+id/tv_note”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_below=“@+id/tv_nickname”

android:layout_centerHorizontal=“true”

android:layout_marginTop=“8dp”

android:text=“兴趣:Android、Java、Kotlin”

android:textColor=“#000”

android:textSize=“16sp” />

<androidx.appcompat.widget.Toolbar

android:id=“@+id/toolbar”

android:layout_width=“match_parent”

android:layout_height=“?attr/actionBarSize”

app:contentInsetStart=“0dp”

app:layout_collapseMode=“pin”

app:layout_scrollFlags=“scroll|snap” />

</com.google.android.material.appbar.CollapsingToolbarLayout>

</com.google.android.material.appbar.AppBarLayout>

<androidx.core.widget.NestedScrollView

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:layout_marginTop=“4dp”

android:fillViewport=“true”

android:orientation=“vertical”

android:overScrollMode=“never”

app:layout_behavior=“@string/appbar_scrolling_view_behavior”>

<LinearLayout

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:orientation=“vertical”>

<com.google.android.material.tabs.TabLayout

android:id=“@+id/tab_layout”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:background=“#FFF”

app:tabIndicatorFullWidth=“false”

app:tabRippleColor=“#00000000” />

<androidx.viewpager.widget.ViewPager

android:id=“@+id/view_pager”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:layout_marginTop=“1dp” />

</androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

根布局是CoordinatorLayout(协调布局),该布局主要是两个部分,AppBarLayout和NestedScrollView,通过协调布局可以让里面的子布局形成联动效果。你现在对这个可能还不了解,但是在你看到效果图之后你就会知道是怎么回事了。

这里面有一个icon_man图标是白色的,我贴了你也看不见,所以你可以自己找一个图标,或者从我的源码里去拿。

② 创建Fragment

这里的Fragment我们依然采用动态生成的方式,不同的是Fragment里面现在放的是RecyclerView,在mode5包下新建一个PersonTabFragment,布局是fragment_person_tab.xml,里面的代码:

<?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:gravity=“center”

android:orientation=“vertical”>

<androidx.recyclerview.widget.RecyclerView

android:id=“@+id/rv”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:overScrollMode=“never” />

而使用RecyclerView自然要准备适配器,那么也就需要列表的item布局了,先在layout下创建item_person_type_rv.xml,里面的代码如下:

<?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=“wrap_content”

android:layout_marginBottom=“1dp”

android:orientation=“vertical”

android:background=“#FFF”

android:padding=“16dp”>

<TextView

android:id=“@+id/tv_title”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:text=“内容”

android:textColor=“#000”

android:textSize=“16sp”

android:textStyle=“bold” />

<TextView

android:id=“@+id/tv_content”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_marginTop=“8dp”

android:text=“内容”

android:textColor=“#000”

android:textSize=“14sp” />

然后在adapter包下新建一个PersonTypeAdapter,再写里面的代码之前,先添加一个依赖库,打开项目的build.gradle,在allprojects{}的repositories{}中添加如下依赖库。

maven {url “https://jitpack.io”}

在这里插入图片描述

然后打开app下的build.gradle,在下dependencies{}闭包下添加如下依赖:

//RecyclerView最好的适配器,让你的适配器一目了然,告别代码冗余

implementation ‘com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30’

然后Sync Now。

下面来写PersonTypeAdapter的代码:

package com.llw.tablayoutdemo.adapter;

import androidx.annotation.Nullable;

import com.chad.library.adapter.base.BaseQuickAdapter;

import com.chad.library.adapter.base.BaseViewHolder;

import com.llw.tablayoutdemo.R;

import java.util.List;

public class PersonTypeAdapter extends BaseQuickAdapter<String, BaseViewHolder> {

public PersonTypeAdapter(int layoutResId, @Nullable List data) {

super(layoutResId, data);

}

@Override

protected void convert(BaseViewHolder helper, String item) {

helper.setText(R.id.tv_title,item)

.setText(R.id.tv_content,item+“内容”);

}

}

再回来PersonTabFragment中,修改里面的代码后如下:

public class PersonTabFragment extends Fragment {

private String content;

private RecyclerView rv;

private List typeList = new ArrayList<>();

public PersonTabFragment(String content) {

this.content = content;

}

@Override

public View onCreateView(final LayoutInflater inflater,

ViewGroup container, Bundle savedInstanceState) {

final View view = inflater.inflate(R.layout.fragment_person_tab,

container, false);

rv = view.findViewById(R.id.rv);

return view;

}

@Override

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

int num = new Random().nextInt(30);

typeList.clear();

for (int i = 0; i < num; i++) {

typeList.add(content + i);

}

LinearLayoutManager layoutManager = new LinearLayoutManager(this.getActivity());

PersonTypeAdapter personTypeAdapter = new PersonTypeAdapter(R.layout.item_person_type_rv, typeList);

rv.setLayoutManager(layoutManager);

rv.setAdapter(personTypeAdapter);

}

}

③ 图片模糊

由于个人主页面的背景图我希望是模糊的,所以需要用到一个工具类,在mode5下新建一个ImageFilter,里面的代码如下:

/**

  • 图片模糊

*/

public class ImageFilter {

//图片缩放比例

private static final float BITMAP_SCALE = 0.4f;

/**

  • 模糊图片的具体方法

  • @param context 上下文对象

  • @param image 需要模糊的图片

  • @return 模糊处理后的图片

*/

public static Bitmap blurBitmap(Context context, Bitmap image, float blurRadius) {

// 计算图片缩小后的长宽

int width = Math.round(image.getWidth() * BITMAP_SCALE);

int height = Math.round(image.getHeight() * BITMAP_SCALE);

// 将缩小后的图片做为预渲染的图片

Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);

// 创建一张渲染后的输出图片

Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);

// 创建RenderScript内核对象

RenderScript rs = RenderScript.create(context);

// 创建一个模糊效果的RenderScript的工具对象

ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));

// 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间

// 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去

Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);

Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);

// 设置渲染的模糊程度, 25f是最大模糊度

blurScript.setRadius(blurRadius);

// 设置blurScript对象的输入内存

blurScript.setInput(tmpIn);

// 将输出数据保存到输出内存中

blurScript.forEach(tmpOut);

// 将数据填充到Allocation中

tmpOut.copyTo(outputBitmap);

return outputBitmap;

}

public static Bitmap blurBitmap(Context context, Bitmap bitmap) {

//用需要创建高斯模糊bitmap创建一个空的bitmap

Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);

// 初始化Renderscript,该类提供了RenderScript context,创建其他RS类之前必须先创建这个类,其控制RenderScript的初始化,资源管理及释放

RenderScript rs = RenderScript.create(context);

// 创建高斯模糊对象

ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));

// 创建Allocations,此类是将数据传递给RenderScript内核的主要方 法,并制定一个后备类型存储给定类型

Allocation allIn = Allocation.createFromBitmap(rs, bitmap);

Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);

//设定模糊度(注:Radius最大只能设置25.f)

blurScript.setRadius(15.f);

// Perform the Renderscript

blurScript.setInput(allIn);

blurScript.forEach(allOut);

// Copy the final bitmap created by the out Allocation to the outBitmap

allOut.copyTo(outBitmap);

// recycle the original bitmap

// bitmap.recycle();

// After finishing everything, we destroy the Renderscript.

rs.destroy();

return outBitmap;

}

}

这个工具类的代码我也是从网络上找的。

④ 编码运行

下面回到之前的PersonActivity,里面的代码如下:

package com.llw.tablayoutdemo.mode5;

import androidx.appcompat.app.AppCompatActivity;

import androidx.fragment.app.Fragment;

import androidx.viewpager.widget.ViewPager;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Color;

import android.os.Bundle;

import android.widget.ImageView;

import android.widget.LinearLayout;

import android.widget.Toast;

import com.google.android.material.appbar.AppBarLayout;

import com.google.android.material.appbar.CollapsingToolbarLayout;

import com.google.android.material.tabs.TabLayout;

import com.llw.tablayoutdemo.R;

import com.llw.tablayoutdemo.adapter.GoodsFragmentAdapter;

import com.llw.tablayoutdemo.mode4.GoodsTabFragment;

import java.util.ArrayList;

import java.util.Random;

/**

  • 个人主页面 :CoordinatorLayout + NestedScrollView + TabLayout + ViewPager + Fragment + RecyclerView

  • @author llw

*/

public class PersonActivity extends AppCompatActivity {

private ImageView ivBg;

private CollapsingToolbarLayout collapsingToolbarLayout;

private AppBarLayout appBarLayout;

private TabLayout tabLayout;

private ViewPager viewPager;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_person);

ivBg = findViewById(R.id.iv_bg);

collapsingToolbarLayout = findViewById(R.id.toolbar_layout);

相关文章:

Android TabLayout 使用进阶(含源码)

android:layout_height“match_parent” android:orientation“vertical” tools:context“.mode2.ClassificationActivity”> <com.google.android.material.tabs.TabLayout android:id“id/tab_layout” android:layout_width“match_parent” android:layout_he…...

数据库系统概论的第六版与第五版的区别,附pdf

我用夸克网盘分享了「数据库系统概论第五六版资源」&#xff0c;点击链接即可保存。 链接&#xff1a;https://pan.quark.cn/s/21a278378dee 第6版教材修订的主要内容 为了保持科学性、先进性和实用性&#xff0c;在第5版教材基础上对全书内容进行了修改、更新和充实。 在科…...

管理etcd的存储空间配额

如何管理etcd的存储空间配额 - 防止集群存储耗尽指南 本文基于etcd v3.4官方文档编写 为什么需要空间配额&#xff1f; 在分布式系统中&#xff0c;etcd作为可靠的键值存储&#xff0c;很容易成为系统瓶颈。当遇到以下情况时&#xff1a; 应用程序频繁写入大量数据未及时清理…...

深入浅出 NRM:加速你的 npm 包管理之旅

文章目录 前言一、NRM 是什么&#xff1f;二、为什么需要 NRM&#xff1f;三、NRM 的优势四、NRM 的安装与使用4.1 安装 NRM4.2 查看可用的 npm 源4.3 切换 npm 源4.4 测试 npm 源速度4.5 添加自定义 npm 源4.6 删除 npm 源 五、NRM 的进阶使用六、总结 前言 作为一名 JavaScr…...

ESP32开发学习记录---》GPIO

she 2025年2月5日&#xff0c;新年后决定开始充电提升自己&#xff0c;故作此记,以前没有使用过IDF开发ESP32因此新年学习一下ESP32。 ESPIDF开发环境配置网上已经有很多的资料了&#xff0c;我就不再赘述&#xff0c;我这里只是对我的学习经历的一些记录。 首先学习一个…...

stm32点灯 GPIO的输出模式

目录 1.选择RCC时钟 2.SYS 选择调试模式 SW 3.GPIO 配置 4.时钟树配置&#xff08; 默认不变&#xff09;HSI 高速内部时钟8Mhz 5.项目配置 6.代码 延时1s循环LED亮灭 1.选择RCC时钟 2.SYS 选择调试模式 SW 3.GPIO 配置 4.时钟树配置&#xff08; 默认不变&#xff09…...

[paddle] 矩阵的分解

特征值 设 A A A 是一个 n n n \times n nn 的方阵&#xff0c; λ \lambda λ 是一个标量&#xff0c; v \mathbf{v} v 是一个非零向量。如果满足以下方程&#xff1a; A v λ v A\mathbf{v} \lambda\mathbf{v} Avλv 则称 λ \lambda λ 为矩阵 A A A 的一个 特征值…...

【基于SprintBoot+Mybatis+Mysql】电脑商城项目之修改密码和个人资料

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【Spring篇】【计算机网络】【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f383;1.修改密码 -持久…...

【深度学习】DataLoader自定义数据集制作

第一步 导包 import os import matplotlib.pyplot as plt %matplotlib inline import numpy as np import torch from torch import nn import torch.optim as optim import torchvision from torchvision import transforms,models,datasets import imageio import time impo…...

【Elasticsearch】Geo-distance聚合

geo_distance聚合的形状是圆形。它基于一个中心点&#xff08;origin&#xff09;和一系列距离范围来计算每个文档与中心点的距离&#xff0c;并将文档分配到相应的距离范围内。这种聚合方式本质上是以中心点为圆心&#xff0c;以指定的距离范围为半径的圆形区域来划分数据。 为…...

【R语言】apply函数族

在R语言中使用循环操作时是使用自身来实现的&#xff0c;效率较低。所以R语言有一个符合其统计语言出身的特点&#xff1a;向量化。R语言中的向量化运用了底层的C语言&#xff0c;而C语言的效率比高层的R语言的效率高。 apply函数族主要是为了解决数据向量化运算的问题&#x…...

Vue - shallowRef 和 shallowReactive

一、shallowRef 和 shallowReactive &#xff08;一&#xff09;shallowRef 在 Vue 3 中&#xff0c;shallowRef 是一个用于创建响应式引用的 API&#xff0c;它与 ref 相似&#xff0c;但它只会使引用的基本类型&#xff08;如对象、数组等&#xff09;表现为响应式&#xf…...

双目标定与生成深度图

基于C#联合Halcon实现双目标定整体效果 一&#xff0c;标定 1&#xff0c;标定前准备工作 &#xff08;获取描述文件与获取相机参数&#xff09; 针对标准标定板可以直接调用官方提供描述文件&#xff0c;也可以自己生成描述文件后用PS文件打印 2&#xff0c;相机标定 &…...

实名制-网络平台集成身份证实名认证接口/身份证查询-PHP

在当今数字化快速发展的时代&#xff0c;线上平台的安全性和用户体验成为了衡量其成功与否的关键因素。其中&#xff0c;身份证实名认证接口的集成显得尤为重要&#xff0c;它不仅为用户提供了更加安全、可靠的网络环境&#xff0c;同时也增强了平台的信任度和合规性。 对于任…...

全面解析机器学习优化算法中的进化策略

全面解析机器学习优化算法中的进化策略 全面解析机器学习优化算法中的进化策略引言什么是进化策略?基本概念核心组件算法流程数学基础高斯扰动期望值更新与其他优化方法的比较梯度下降法(Gradient Descent, GD)遗传算法(Genetic Algorithm, GA)Python案例基本实现改进版:…...

go数据结构学习笔记

本博文较为完整的实现了go的链表、栈&#xff0c;队列&#xff0c;树&#xff0c;排序&#xff0c;链表包括顺序链表&#xff0c;双向链表&#xff0c;循环链表&#xff0c;队列是循环队列&#xff0c;排序包含冒牌、选择 1.链表 1.1 顺序链表 type LNode struct {data intn…...

【深度学习】DeepSeek模型介绍与部署

原文链接&#xff1a;DeepSeek-V3 1. 介绍 DeepSeek-V3&#xff0c;一个强大的混合专家 (MoE) 语言模型&#xff0c;拥有 671B 总参数&#xff0c;其中每个 token 激活 37B 参数。 为了实现高效推理和成本效益的训练&#xff0c;DeepSeek-V3 采用了多头潜在注意力 (MLA) 和 De…...

使用SpringBoot发送邮件|解决了部署时连接超时的bug|网易163|2025

使用SpringBoot发送邮件 文章目录 使用SpringBoot发送邮件1. 获取网易邮箱服务的授权码2. 初始化项目maven部分web部分 3. 发送邮件填写配置EmailSendService [已解决]部署时连接超时附&#xff1a;Docker脚本Dockerfile创建镜像启动容器 1. 获取网易邮箱服务的授权码 温馨提示…...

【工具篇】深度揭秘 Midjourney:开启 AI 图像创作新时代

家人们,今天咱必须好好唠唠 Midjourney 这个在 AI 图像生成领域超火的工具!现在 AI 技术发展得那叫一个快,各种工具层出不穷,Midjourney 绝对是其中的明星产品。不管你是专业的设计师、插画师,还是像咱这种对艺术创作有点小兴趣的小白,Midjourney 都能给你带来超多惊喜,…...

构成正方形的数量:算法深度剖析与实践

目录 引言算法核心概念 定义正方形的构成条件数据结构与输入形式算法数学原理 几何关系的数学表达坐标运算与判定逻辑Python 实现 代码展示代码解析Python 实现的优势与局限C 语言实现 代码展示代码解析C 语言实现的性能特点性能分析与优化 性能分析 时间复杂度空间复杂度优化思…...

Spring设计模式(9种)(详细篇)

总体分为三大类&#xff1a; 创建型模式&#xff1a;工厂方法模式、单例模式。 结构型模式&#xff1a;适配器模式、代理模式、装饰器模式。 行为型模式&#xff1a;观察者模式、策略模式、模板方法模式。 一、简单工厂模式&#xff08;Simple Factory&#xff09; 概述&…...

使用Express.js和SQLite3构建简单TODO应用的后端API

使用Express.js和SQLite3构建简单TODO应用的后端API 引言环境准备代码解析1. 导入必要的模块2. 创建Express应用实例3. 设置数据库连接4. 初始化数据库表5. 配置中间件6. 定义数据接口7. 定义路由7.1 获取所有TODO项7.2 创建TODO项7.3 更新TODO项7.4 删除TODO项 8. 启动服务器 …...

2025年2月6日(anaconda cuda 学习 基本命令)

查看电脑的显卡型号是否支持CUDA的安装 https://developer.nvidia.com/zh-cn/cuda-gpus 查看可以安装的CUDA版本 https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html CUDA安装地址 https://developer.nvidia.com/cuda-toolkit-archive Anaconda下载地址 htt…...

大数据方向知识图谱及发展前景分析

目录 一、知识体系 二、大数据领域前景分析&#xff1a; 1. 市场需求 2. 技术趋势 3. 职业发展路径 4. 学习路线建议 5. 推荐认证体系 一、知识体系 大数据知识体系 ├── 基础理论 │ ├── 数学基础&#xff1a;概率统计、线性代数、离散数学 │ ├── 计算机基…...

Docker深度解析:安装各大环境

安装 Nginx 实现负载均衡&#xff1a; 挂载 nginx html 文件&#xff1a; 创建过载目录&#xff1a; mkdir -p /data/nginx/{conf,conf.d,html,logs} 注意&#xff1a;在挂载前需要对 conf/nginx.conf 文件进行编写 worker_processes 1;events {worker_connections 1024; …...

Verilog语言学习总结

Verilog语言学习&#xff01; 目录 文章目录 前言 一、Verilog语言是什么&#xff1f; 1.1 Verilog简介 1.2 Verilog 和 C 的区别 1.3 Verilog 学习 二、Verilog基础知识 2.1 Verilog 的逻辑值 2.2 数字进制 2.3 Verilog标识符 2.4 Verilog 的数据类型 2.4.1 寄存器类型 2.4.2 …...

K8S Deployment 实现 蓝绿 发布

一、何为蓝绿发布 蓝绿发布&#xff08;Blue - Green Deployment&#xff09;是一种软件部署策略&#xff0c;旨在最大程度减少应用程序停机时间&#xff0c;确保新老版本系统平稳过渡。以下为详细介绍&#xff1a; 1.1、基本概念 存在两个完全相同的生产环境&#xff0c;通…...

2025新鲜出炉--前端面试题(一)

文章目录 1. vue3有用过吗, 和vue2之间有哪些区别2. vue-router有几种路由, 分别怎么实现3. webpack和rollup这两个什么区别, 你会怎么选择4. 你能简单介绍一下webpack项目的构建流程吗5. webpack平时有手写过loader和plugin吗6. webpack这块你平时做过哪些优化吗&#xff1f;7…...

【ArcGIS Pro 简介1】

ArcGIS Pro 是由 Esri &#xff08;Environmental Systems Research Institute&#xff09;公司开发的下一代桌面地理信息系统&#xff08;GIS&#xff09;软件&#xff0c;是传统 ArcMap 的现代化替代产品。它结合了强大的空间分析能力、直观的用户界面和先进的三维可视化技术…...

CentOS 6.5编译Rsyslog 8.1903.0

个人博客地址&#xff1a;CentOS 6.5编译Rsyslog 8.1903.0 | 一张假钞的真实世界 个人很早之前的博文&#xff0c;迁移至此作为历史记录。 源码下载参考我的另外一片博文&#xff1a;CentOS 7.3 编译 Rsyslog 8.1903.0。 本篇博文从创建构建环境开始填坑/(ㄒoㄒ)/~~。通过上…...

SQLAlchemy-2.0中模型定义和alembic的数据库迁移工具

SQLAlchemy-2.0中模型定义和alembic的数据库迁移工具 一、SQLAIchemy的介绍二、数据库引擎1、支持的数据库1.1、sqlite数据库1.2、MySQL数据库1.3、数据库引擎的参数 三、定义模型类1、定义模型2、engine负责数据库迁移 四、alembic数据库迁移⼯具1、安装alembic2、初始化alemb…...

两种文件类型(pdf/图片)打印A4半张纸方法

环境:windows10、Adobe Reader XI v11.0.23 Pdf: 1.把内容由横排变为纵排&#xff1a; 2.点击打印按钮&#xff1a; 3.选择打印页范围和多页&#xff1a; 4.内容打印在纸张上部 图片&#xff1a; 1.右键图片点击打印&#xff1a; 2.选择打印类型&#xff1a; 3.打印配置&am…...

【React】合成事件语法

React 合成事件是 React 为了处理浏览器之间的事件差异而提供的一种跨浏览器的事件系统。它封装了原生的 DOM 事件&#xff0c;提供了一致的事件处理机制。 合成事件与原生事件的区别&#xff1a; 合成事件是 React 自己实现的&#xff0c;封装了原生事件。合成事件依然可以通…...

深入解析:如何利用 Python 爬虫获取商品 SKU 详细信息

在电商领域&#xff0c;SKU&#xff08;Stock Keeping Unit&#xff0c;库存单位&#xff09;详细信息是电商运营的核心数据之一。它不仅包含了商品的规格、价格、库存等关键信息&#xff0c;还直接影响到库存管理、价格策略和市场分析等多个方面。本文将详细介绍如何利用 Pyth…...

Unity 加载OSGB(webgl直接加载,无需转换格式!)

Unity webgl加载倾斜摄影数据 前言效果图后续不足 前言 Unity加载倾斜摄影数据&#xff0c;有很多的插件方便好用&#xff0c;但是发布到网页端均失败&#xff0c;因为webgl 的限制&#xff0c;IO读取失效。 前不久发现一个开源项目: UnityOSGB-main 通过两种方式在 Unity 中…...

Maven架构项目管理工具

1.1什么是Maven 翻译为“专家”&#xff0c;“内行”Maven是跨平台的项目管理工具。主要服务于基于Java平台的项目构建&#xff0c;依赖管理和项目信息管理。什么是理想的项目构建&#xff1f; 高度自动化&#xff0c;跨平台&#xff0c;可重用的组件&#xff0c;标准化的 什么…...

【漫画机器学习】083.安斯库姆四重奏(Anscombe‘s Quartet)

安斯库姆四重奏&#xff08;Anscombes Quartet&#xff09; 1. 什么是安斯库姆四重奏&#xff1f; 安斯库姆四重奏&#xff08;Anscombes Quartet&#xff09;是一组由统计学家弗朗西斯安斯库姆&#xff08;Francis Anscombe&#xff09; 在 1973 年 提出的 四组数据集。它们…...

【梦想终会实现】Linux驱动学习5

加油加油坚持住&#xff01; 1、 Linux驱动模型&#xff1a;驱动模型即将各模型中共有的部分抽象成C结构体。Linux2.4版本前无驱动模型的概念&#xff0c;每个驱动写的代码因人而异&#xff0c;随后为规范书写方式&#xff0c;发明了驱动模型&#xff0c;即提取公共信息组成一…...

QT修仙之路1-1--遇见QT

文章目录 遇见QT二、QT概述2.1 定义与功能2.2 跨平台特性2.3 优点汇总 三、软件安装四、QT工具介绍(重要)4.1 Assistant4.2 Designer4.3 uic.exe4.4 moc.exe4.5 rcc.exe4.6 qmake4.7 QTcreater 五、QT工程项目解析(作业)5.1 配置文件&#xff08;.pro&#xff09;5.2 头文件&am…...

【实战篇】Android安卓本地离线实现视频检测人脸

实战篇Android安卓本地离线实现视频检测人脸 引言项目概述核心代码类介绍人脸检测流程项目地址总结 引言 在当今数字化时代&#xff0c;人脸识别技术已经广泛应用于各个领域&#xff0c;如安防监控、门禁系统、移动支付等。本文将以第三视角详细讲解如何基于bifan-wei-Face/De…...

【图像处理】- 基本图像操作

基本图像操作详解 基本图像操作是图像处理的基础&#xff0c;涵盖了对图像进行简单但重要的变换。以下是几种常见的基本图像操作及其详细说明&#xff1a; 1. 裁剪 (Cropping) 描述&#xff1a;从原始图像中提取一个矩形区域。 实现方法&#xff1a; 使用图像的坐标系指定…...

代码随想录算法训练营| 二叉树总结

代码随想录 二叉树的理论基础&#xff1a;二叉树种类、存储方式、遍历方式、定义方式 二叉树遍历&#xff1a;深度优先和广度优先 二叉树属性&#xff1a;对称、深度、节点、平衡、路径、回溯 修改与构造&#xff1a;反转、构造、合并 涉及到二叉树的构造&#xff0c;无论普…...

Sentinel的安装和做限流的使用

一、安装 Release v1.8.3 alibaba/Sentinel GitHubA powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件) - Release v1.8.3 alibaba/Sentinelhttps://github.com/alibaba/Senti…...

八、Spring Boot 日志详解

目录 一、日志的用途 二、日志使用 2.1 打印日志 2.1.1 在程序中获取日志对象 2.1.2 使用日志对象打印日志 2.2、日志框架介绍 2.2.1 门面模式(外观模式) 2.2.2 门面模式的实现 2.2.3 SLF4J 框架介绍 2.3 日志格式的说明 2.4 日志级别 2.4.1 日志级别的分类 2.4.2…...

vue2:如何动态控制el-form-item之间的行间距

需求 某页面有查看和编辑两种状态: 编辑: 查看: 可以看到,查看时,行间距太大导致页面不紧凑,所以希望缩小查看是的行间距。 行间距设置 行间距通常是通过 CSS 的 margin 或 padding 属性来控制的。在 Element UI 的样式表中,.el-form-item 的下边距(margin-bottom)…...

智能门铃市场:开启智能家居新时代

在科技日新月异的今天&#xff0c;智能家居产品正以前所未有的速度融入我们的生活&#xff0c;而智能门铃作为其中的重要一员&#xff0c;不仅为我们的家居生活带来了极大的便利&#xff0c;更在安全方面提供了坚实的保障。它就像一位忠诚的守门人&#xff0c;无论白天黑夜&…...

C基础寒假练习(6)

一、终端输入行数&#xff0c;打印倒金字塔 #include <stdio.h> int main() {int rows;printf("请输入倒金字塔的行数: ");scanf("%d", &rows);for (int i rows; i > 0; i--) {// 打印空格for (int j 0; j < rows - i; j) {printf(&qu…...

【深度学习】基于MXNet的多层感知机的实现

多层感知机 结构组成 大致由三层组成&#xff1a;输入层-隐藏层-输出层&#xff0c;其中隐藏层大于等于一层 其中&#xff0c;隐藏层和输出层都是全连接 隐藏层的层数和神经元个数也是超参数 多层隐藏层&#xff0c;在本质上仍等价于单层神经网络&#xff08;可从输出方程…...

Mac 终端命令大全

—目录操作— ꔷ mkdir 创建一个目录 mkdir dirname ꔷ rmdir 删除一个目录 rmdir dirname ꔷ mvdir 移动或重命名一个目录 mvdir dir1 dir2 ꔷ cd 改变当前目录 cd dirname ꔷ pwd 显示当前目录的路径名 pwd ꔷ ls 显示当前目录的内容 ls -la ꔷ dircmp 比较两个目录的内容 di…...

【蓝桥杯嵌入式】2_LED

1、电路图 74HC573是八位锁存器&#xff0c;当控制端LE脚为高电平时&#xff0c;芯片“导通”&#xff0c;LE为低电平时芯片“截止”即将输出状态“锁存”&#xff0c;led此时不会改变状态&#xff0c;所以可通过led对应的八个引脚的电平来控制led的状态&#xff0c;原理图分析…...