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、

 

posted @ 2022-09-05 17:11  熊猫Panda先生  阅读(278)  评论(0编辑  收藏  举报