Android控件
一、图片轮播 com.youth.banner.Banner
1、需要在libs文件夹下导入三个包:
- banner-2.1.0.arr
- glide-4.10.0.aar // Android 上的图片加载和缓存库,其目的是实现平滑的图片列表滚动效果
- gifdecoder-4.10.0.aar
以上三个文件已经打包放在libs.zip中供大家下载,先解压缩保存到app/libs文件夹下,然后在app/build.gradle文件添加依赖:
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
或者使用中心仓库的依赖
// glide
implementation 'com.github.bumptech.glide:glide:4.10.0'
// banner
implementation 'com.youth.banner:banner:2.1.0'
2、创建布局文件activity_guide.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=".activity.GuideActivity"> <com.youth.banner.Banner android:id="@+id/banGuide" android:layout_width="match_parent" android:layout_height="match_parent" app:banner_indicator_marginBottom="70dp" app:banner_indicator_normal_color="@color/white" app:banner_indicator_normal_width="10dp" app:banner_indicator_selected_color="@color/blue" app:banner_indicator_selected_width="10dp" app:banner_indicator_space="10dp" /> </RelativeLayout>
3、编辑GuideActivity.java文件
使用两种图片来源:
(1)使用本地drawable文件夹下的资源
我们提前在res/drawable文件夹下保存了5张引导页图片
package com.sdbi.smartcityli01.activity; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import com.bumptech.glide.Glide; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.request.RequestOptions; import com.sdbi.smartcityli01.R; import com.youth.banner.Banner; import com.youth.banner.adapter.BannerImageAdapter; import com.youth.banner.holder.BannerImageHolder; import com.youth.banner.indicator.CircleIndicator; import java.util.ArrayList; import java.util.List; public class GuideActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "GuideActivity"; private Banner banGuide; private int[] guideImages = {R.drawable.guide1, R.drawable.guide2, R.drawable.guide3, R.drawable.guide4, R.drawable.guide5}; private List<Integer> imagesList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_guide); initView(); imagesList = new ArrayList<Integer>(); for (int i = 0; i < guideImages.length; i++) { imagesList.add(guideImages[i]); } banGuide.setAdapter(new BannerImageAdapter<Integer>(imagesList) { @Override public void onBindView(BannerImageHolder bannerImageHolder, Integer integer, int i, int i1) { Glide.with(GuideActivity.this) .load(integer) .into(bannerImageHolder.imageView); } }).addBannerLifecycleObserver(this).setIndicator(new CircleIndicator(this)).isAutoLoop(false); } private void initView() { banGuide = (Banner) findViewById(R.id.banGuide); } @Override public void onClick(View view) { } }
(2)使用网络图片资源
package com.sdbi.smartcityli01.activity; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import com.bumptech.glide.Glide; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.request.RequestOptions; import com.sdbi.smartcityli01.R; import com.youth.banner.Banner; import com.youth.banner.adapter.BannerImageAdapter; import com.youth.banner.holder.BannerImageHolder; import com.youth.banner.indicator.CircleIndicator; import java.util.ArrayList; import java.util.List; public class GuideActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "GuideActivity"; private Banner banGuide; private int[] guideImages = {R.drawable.guide1, R.drawable.guide2, R.drawable.guide3, R.drawable.guide4, R.drawable.guide5}; private List<String> imgList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_guide); initView();
// 从网上找了几张手机桌面测试 imgList.add("https://c-ssl.dtstatic.com/uploads/item/202007/19/20200719145236_BTCck.thumb.1000_0.jpeg"); imgList.add("https://c-ssl.dtstatic.com/uploads/item/202004/13/20200413191327_wkirm.thumb.1000_0.jpeg"); imgList.add("https://c-ssl.dtstatic.com/uploads/item/202107/23/20210723202754_8UCda.thumb.1000_0.jpeg"); imgList.add("https://c-ssl.dtstatic.com/uploads/blog/202105/15/20210515104524_26bf1.thumb.1000_0.jpg"); imgList.add("https://c-ssl.dtstatic.com/uploads/blog/202205/11/20220511171657_fa181.thumb.1000_0.jpeg"); banGuide.setAdapter(new BannerImageAdapter<String>(imgList) { @Override public void onBindView(BannerImageHolder holder, String s, int i, int i1) { Log.d(TAG, "onBindView: s = " + s); Glide.with(GuideActivity.this) .load(s) .into(holder.imageView); } }).addBannerLifecycleObserver(this).setIndicator(new CircleIndicator(this)).isAutoLoop(false); } private void initView() { banGuide = (Banner) findViewById(R.id.banGuide); } @Override public void onClick(View view) { } }
注意,在清单文件AndroidManifest.xml文件中添加网络访问权限
<uses-permission android:name="android.permission.INTERNET" />
4、设置全屏显式
我们可以通过修改AndroidManifest.xml文件中应用程序的主题样式(android:theme属性)来实现全屏显示。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.sdbi.smartcityli01"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.SmartCityLi01" tools:targetApi="31"> <activity android:name=".activity.GuideActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".MainActivity" android:exported="true"></activity> </application> </manifest>
首先,我们先在themes.xml文件中增加自定义主题。
<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> <style name="Theme.SmartCityLi01" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <!-- Primary brand color. --> <item name="colorPrimary">@color/purple_500</item> <item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorOnPrimary">@color/white</item> <!-- Secondary brand color. --> <item name="colorSecondary">@color/teal_200</item> <item name="colorSecondaryVariant">@color/teal_700</item> <item name="colorOnSecondary">@color/black</item> <!-- Status bar color. --> <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> <!-- Customize your theme here. --> </style> <style name="MyTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="android:windowTranslucentStatus">true</item> <item name="colorPrimary">@color/blue</item> <item name="colorPrimaryDark">@color/blue</item> <item name="colorAccent">@color/blue</item> </style> </resources>
其次,我们修改AndroidManifest.xml文件中<application>标签的android:theme属性值,引用我们自定义的主题样式。
android:theme="@style/MyTheme"
运行,查看效果。
5、轮播图滑动切换
通过addOnPageChangeListener()方法给Banner添加滑动事件,滑动事件监听器要实现OnPageChangeListener接口,实现三个方法:
- onPageScrolled()
- onPageSelected()
- onPageScrollStateChanged()
(1)onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为:
- position :当前页面,即你点击滑动的页面(从A滑B,则是A页面的position。官方说明:Position index of the first page currently being displayed. Page position+1 will be visible if positionOffset is nonzero.)
- positionOffset:当前页面偏移的百分比
- positionOffsetPixels:当前页面偏移的像素位置
(2)onPageSelected(int position)
此方法是页面跳转完后得到调用,position是你当前选中的页面的Position(位置编号)(从A滑动到B,就是B的position)。
(3)onPageScrollStateChanged(int state)
此方法是在状态改变的时候调用,其中state这个参数有三种状态:
- SCROLL_STATE_DRAGGING(1)表示用户手指“按在屏幕上并且开始拖动”的状态(手指按下但是还没有拖动的时候还不是这个状态,只有按下并且手指开始拖动后log才打出。)
- SCROLL_STATE_IDLE(0)滑动动画做完的状态。
- SCROLL_STATE_SETTLING(2)在“手指离开屏幕”的状态。
一个完整的滑动动作,三种状态的出发顺序为(1,2,0)。
我们通过日志输出,观察一下这三个方法的调用情况。
// 给Banner添加滑动事件 banGuide.addOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // 当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用 Log.d(TAG, "onPageScrolled: position = " + position + "; positionOffset = " + positionOffset + "; positionOffsetPixels = " + positionOffsetPixels); } @Override public void onPageSelected(int position) { // 此方法是页面跳转完后得到调用,position是你当前选中的页面的Position(位置编号)(从A滑动到B,就是B的position) Log.d(TAG, "onPageSelected: position = " + position); } @Override public void onPageScrollStateChanged(int state) { // 此方法是在状态改变的时候调用 Log.d(TAG, "onPageScrollStateChanged: state = " + state); } });
我们如果要在滑动到最后一页显示出两个Button,我们可以这样来修改代码:
// 给Banner添加滑动事件 banGuide.addOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // 当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用 } @Override public void onPageSelected(int position) { // 此方法是页面跳转完后得到调用,position是你当前选中的页面的Position(位置编号)(从A滑动到B,就是B的position) Log.d(TAG, "onPageSelected: position = " + position); if (position == imgList.size() - 1) { btnEntry.setVisibility(View.VISIBLE); btnNet.setVisibility(View.VISIBLE); } else { btnEntry.setVisibility(View.INVISIBLE); btnNet.setVisibility(View.INVISIBLE); } } @Override public void onPageScrollStateChanged(int state) { // 此方法是在状态改变的时候调用 } });
6、Glide介绍
Glide是Google主导的图片加载开源库。
可以本地添加库文件,也可以通过添加在线依赖。
implementation 'com.github.bumptech.glide:glide:4.14.2'
(1)基本加载
Glide.with(context)
.load(url)
.into(imageView);
关键就是三步走:先with(),再load(),最后into()。
首先,调用Glide.with()方法用于创建一个加载图片的实例。
with()方法可以接收Context、Activity或者Fragment类型的参数。
也就是说我们选择的范围非常广,不管是在Activity还是Fragment中调用with()方法,都可以直接传this。
那如果调用的地方既不在Activity中也不在Fragment中呢?
也没关系,我们可以获取当前应用程序的ApplicationContext,传入到with()方法当中。
注意with()方法中传入的实例会决定Glide加载图片的生命周期,如果传入的是Activity或者Fragment的实例,那么当这个Activity或Fragment被销毁的时候,图片加载也会停止。
如果传入的是ApplicationContext,那么只有当应用程序被杀掉的时候,图片加载才会停止。
接下来,看一下load()方法,这个方法用于指定待加载的图片资源。
Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等等。
因此load()方法也有很多个方法重载,除了我们刚才使用的加载一个字符串网址之外,你还可以这样使用load()方法:
// 加载本地图片 File file = new File(getExternalCacheDir() + "/image.jpg"); Glide.with(this).load(file).into(imageView); // 加载应用资源 int resource = R.drawable.image; Glide.with(this).load(resource).into(imageView); // 加载二进制流 byte[] image = getImageBytes(); Glide.with(this).load(image).into(imageView); // 加载Uri对象 Uri imageUri = getImageUri(); Glide.with(this).load(imageUri).into(imageView);
最后,看一下into()方法,这个方法就很简单了,我们希望让图片显示在哪个ImageView上,把这个ImageView的实例传进去就可以了。
当然,into()方法不仅仅是只能接收ImageView类型的参数,还支持很多更丰富的用法。
(2)设置加载中和加载失败的情况
Glide.with(context) .load(url) .placeholder(R.drawable.loading) //占位图,加载中的图片,可放个gif .error(R.drawable.failed) //异常占位图,失败图片 .into(imageView);
占位图,就是指在图片的加载过程中,我们先显示一张临时的图片,等图片加载出来了再替换成要加载的图片。
我们只是在刚才的三步走之间插入了一个placeholder()方法,然后将占位图片的资源id传入到这个方法中即可。
另外,这个占位图的用法其实也演示了Glide当中绝大多数API的用法,其实就是在load()和into()方法之间串接任意想添加的功能就可以了。
不过如果现在重新运行一下代码,很可能是根本看不到占位图效果的。
因为Glide有非常强大的缓存机制,我们刚才加载那张图的时候Glide自动就已经将它缓存下来了,下次加载的时候将会直接从缓存中读取,不会再去网络下载了,因而加载的速度非常快,所以占位图可能根本来不及显示。
因此这里我们还需要稍微做一点修改,来让占位图能有机会显示出来,修改代码如下所示:
Glide.with(context)
.load(url)
.placeholder(R.drawable.loading)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
这里串接了一个diskCacheStrategy()方法,并传入DiskCacheStrategy.NONE参数,这样就可以禁用掉Glide的缓存功能。
异常占位图,就是加载失败时显示的图片。
如果因为某些异常情况导致图片加载失败,比如说手机网络信号不好,这个时候就显示这张异常占位图。
这里又串接了一个error()方法就可以指定异常占位图了。
我们可以将图片的url地址修改成一个不存在的图片地址,或者干脆直接将手机的网络给关了,然后重新运行程序,就可以见到异常占位图了。
(3)指定图片大小
Glide.with(context) .load(url) .asGif() // 只能加载gif文件 .into(imageView);
实际上,使用Glide在绝大多数情况下我们都是不需要指定图片大小的。
在这里,我们还需要先了解一个概念,就是我们平时在加载图片的时候很容易会造成内存浪费。
什么叫内存浪费呢?比如说一张图片的尺寸是1000*1000像素,但是我们界面上的ImageView可能只有200*200像素,这个时候如果你不对图片进行任何压缩就直接读取到内存中,这就属于内存浪费了,因为程序中根本就用不到这么高像素的图片。
而使用Glide,我们就完全不用担心图片内存浪费,甚至是内存溢出的问题。
因为Glide从来都不会直接将图片的完整尺寸全部加载到内存中,而是用多少加载多少。
Glide会自动判断ImageView的大小,然后只将这么大的图片像素加载到内存当中,帮助我们节省内存开支。
不过,如果我们必须给图片指定一个固定的大小,Glide仍然是支持这个功能的。
修改Glide加载部分的代码,如下所示:
Glide.with(context) .load(url) .override(100, 100) .into(imageView);
这里使用override()方法指定了一个图片的尺寸,也就是说,Glide现在只会将图片加载成100*100像素的尺寸,而不会管ImageView的大小是多少了。
(4)添加图片淡入加载的效果
二、VideoView
1、