Android第十三讲笔记(动画入门,开源控件,应用程序打包)

1.动画

在我们的APP中很多地方都会用到动画,像下图的点赞等等都用到了动画在这里插入图片描述
Android 中一共有三种方式来播放动画:
1.帧动画,像一般的 gif 图我们就可以把它看作一个帧动画的过程。在Android中,帧动画的本质是把一组预先准备好的图片循环切换播放,造成一种动画效果。

但是其消耗内存高。

2.补间动画,实际上安卓的大部分动画效果以前都是用补间动画来完成的
3.属性动画,可以说是完全替代了补间动画

我们今天讲的是安卓中使用最多的动画,属性动画。

动画到底由哪些东西组成?

  • 控件透明度发生变化,不透明->透明。
  • 缩放动画,控件大小变化
  • 位移动画,位置发生移动
  • 旋转动画

任何复杂的动画都能由上述几种动画组合而成。

1.案例一–四种简单动画

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="17dp"
        android:text="缩放动画"
        app:layout_constraintStart_toStartOf="@+id/button"
        app:layout_constraintTop_toBottomOf="@+id/button" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="60dp"
        android:text="透明度动画"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="17dp"
        android:layout_marginEnd="3dp"
        android:layout_marginRight="3dp"
        android:text="位移动画"
        app:layout_constraintEnd_toEndOf="@+id/button2"
        app:layout_constraintTop_toBottomOf="@+id/button2" />

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="17dp"
        android:text="旋转动画"
        app:layout_constraintStart_toStartOf="@+id/button3"
        app:layout_constraintTop_toBottomOf="@+id/button3" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_marginStart="32dp"
        android:layout_marginLeft="32dp"
        android:layout_marginTop="35dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:srcCompat="@drawable/love" />

</androidx.constraintlayout.widget.ConstraintLayout>

在这里插入图片描述

MainActivity.java代码

里面实现了简单的动画
ObjectAnimator.ofFloat参数
在这里插入图片描述

1.对哪个对象进行动画操作  2.动画需要执行的属性   3.动画的值的范围
package com.hnucm.android_66;

import androidx.appcompat.app.AppCompatActivity;

import android.animation.ObjectAnimator;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    ImageView imageView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView=findViewById(R.id.imageView);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                透明度动画
//                
                
                ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView,"alpha",1,0);
//                设置动画的时间
                objectAnimator.setDuration(5000);
                objectAnimator.start();
            }
        });

        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                缩放动画
                ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView,"scaleX",0.5f,2);
                objectAnimator.setDuration(5000);
                objectAnimator.start();
            }
        });

        findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                位移动画
                ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView,"translationX",-200,200);
                objectAnimator.setDuration(5000);
                objectAnimator.start();
            }
        });

        findViewById(R.id.button4).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                旋转动画
                ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView,"rotation",0,720);
                objectAnimator.setDuration(5000);
                objectAnimator.start();
            }
        });
    }
}

在这里插入图片描述

2.案例二–复杂动画

实现了透明度的变化和位移的动画
使用方法和上面的简单动画一致

//                组动画
                ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(imageView,"alpha",1,0);
                ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(imageView,"scaleX",0,2);
                ObjectAnimator objectAnimator3 = ObjectAnimator.ofFloat(imageView,"scaleY",0,2);
                AnimatorSet animatorSet = new AnimatorSet();//set中元素不重复
                animatorSet.play(objectAnimator1)
                           .with(objectAnimator2)
                           .with(objectAnimator3);
                animatorSet.setDuration(2000);
                animatorSet.start();

2.开源控件

1.实现圆角+阴影效果(CardView)

<com.google.android.material.card.MaterialCardView
    android:layout_width="0dp"
    android:layout_height="300dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:cardCornerRadius="20dp"
    app:cardElevation="10dp"
    app:layout_constraintTop_toBottomOf="@+id/textView">
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:layout_marginStart="116dp"
            android:layout_marginLeft="116dp"
            android:layout_marginTop="36dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:srcCompat="@drawable/fruit" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="28dp"
            android:text="西瓜"
            android:textSize="20dp"
            app:layout_constraintEnd_toEndOf="@+id/imageView"
            app:layout_constraintStart_toStartOf="@+id/imageView"
            app:layout_constraintTop_toBottomOf="@+id/imageView" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

在CardView中,遵循的是帧布局的原则,没学帧布局的同学,在CardView中直接用一个constraintlayout布局或者自己会用的布局,将需要用的控件包在里面就好了。

app:cardCornerRadius="20dp"
app:cardElevation="10dp"

这两个属性一个是设置圆角,一个是设置阴影。

2.图片选择和相机拍照

1.前期准备(获取权限,导入依赖)

图片选择框架依赖

implementation 'io.github.lucksiege:pictureselector:v2.7.2'

增加文件读写的权限

第三个是照相机的权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />

还要加上这句话

android:requestLegacyExternalStorage="true"

不加可能在高版本的手机上能用,但在低版本上可能用不了
在这里插入图片描述

文件的读写权限和我们以前学习的网络权限等等有些区别,它属于危险权限

危险权限和普通权限

从 Android6.0 开始,Google调整了应用的权限申请方案。调整之后将权限分级,分成了普通权限危险权限

  • 普通权限的授权方式跟之前一样,只需要在 Manifest 文件中申明即可
  • 危险权限不仅需要在 Manifest 文件中声明,还需要在程序中调用官方提供的Api主动申请

危险权限有如下九种:读取联系人;打电话;读取用户日历信息;相机权限;传感器,获取用户位置;读取用户SD卡(拍的照片视频都在这里);多媒体(录音);短信。

在这里插入图片描述
在这里插入图片描述

那么让用户同意权限的弹窗如何调出来呢,我们使用第三方开源控件

首先导入依赖 申请危险权限

implementation 'com.yanzhenjie:permission:2.0.3'

这个开源控件支持的SDK的最低版本是19,所以我们可以修改

在这里插入图片描述

获取权限的案例
AndPermission.with(this)
        .runtime()
        .permission(Permission.WRITE_EXTERNAL_STORAGE, Permission.CAMERA)
        .onGranted(permissions -> {
            Toast.makeText(MainActivity2.this, "授权成功!", Toast.LENGTH_SHORT).show();
        })
        .onDenied(permissions -> {
            //用户拒绝->强制退出app
            Toast.makeText(MainActivity2.this, "未获取权限不能使用本app!", Toast.LENGTH_SHORT).show();
            System.exit(0);
        })
        .start();

在这里插入图片描述

如果用户不允许以上权限,用户拒绝,市面上大多数APP则会直接退出。

2.读取用户相册

建GlideEngine.java类

官方代码,直接使用

package com.hnucm.android_66;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;

import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.BitmapImageViewTarget;
import com.bumptech.glide.request.target.ImageViewTarget;
import com.luck.picture.lib.engine.ImageEngine;
import com.luck.picture.lib.listener.OnImageCompleteCallback;
import com.luck.picture.lib.tools.MediaUtils;
import com.luck.picture.lib.widget.longimage.ImageSource;
import com.luck.picture.lib.widget.longimage.ImageViewState;
import com.luck.picture.lib.widget.longimage.SubsamplingScaleImageView;

 
public class GlideEngine implements ImageEngine {

    /**
     * 加载图片
     *
     * @param context
     * @param url
     * @param imageView
     */
    @Override
    public void loadImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
        Glide.with(context)
                .load(url)
                .into(imageView);
    }

    /**
     * 加载网络图片适配长图方案
     * # 注意:此方法只有加载网络图片才会回调
     *
     * @param context
     * @param url
     * @param imageView
     * @param longImageView
     * @param callback      网络图片加载回调监听 {link after version 2.5.1 Please use the #OnImageCompleteCallback#}
     */
    @Override
    public void loadImage(@NonNull Context context, @NonNull String url,
                          @NonNull ImageView imageView,
                          SubsamplingScaleImageView longImageView, OnImageCompleteCallback callback) {
        Glide.with(context)
                .asBitmap()
                .load(url)
                .into(new ImageViewTarget<Bitmap>(imageView) {
                    @Override
                    public void onLoadStarted(@Nullable Drawable placeholder) {
                        super.onLoadStarted(placeholder);
                        if (callback != null) {
                            callback.onShowLoading();
                        }
                    }

                    @Override
                    public void onLoadFailed(@Nullable Drawable errorDrawable) {
                        super.onLoadFailed(errorDrawable);
                        if (callback != null) {
                            callback.onHideLoading();
                        }
                    }

                    @Override
                    protected void setResource(@Nullable Bitmap resource) {
                        if (callback != null) {
                            callback.onHideLoading();
                        }
                        if (resource != null) {
                            boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(),
                                    resource.getHeight());
                            longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE);
                            imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE);
                            if (eqLongImage) {
                                // 加载长图
                                longImageView.setQuickScaleEnabled(true);
                                longImageView.setZoomEnabled(true);
                                longImageView.setDoubleTapZoomDuration(100);
                                longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
                                longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER);
                                longImageView.setImage(ImageSource.bitmap(resource),
                                        new ImageViewState(0, new PointF(0, 0), 0));
                            } else {
                                // 普通图片
                                imageView.setImageBitmap(resource);
                            }
                        }
                    }
                });
    }

    /**
     * 加载网络图片适配长图方案
     * # 注意:此方法只有加载网络图片才会回调
     *
     * @param context
     * @param url
     * @param imageView
     * @param longImageView
     * @ 已废弃
     */
    @Override
    public void loadImage(@NonNull Context context, @NonNull String url,
                          @NonNull ImageView imageView,
                          SubsamplingScaleImageView longImageView) {
        Glide.with(context)
                .asBitmap()
                .load(url)
                .into(new ImageViewTarget<Bitmap>(imageView) {
                    @Override
                    protected void setResource(@Nullable Bitmap resource) {
                        if (resource != null) {
                            boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(),
                                    resource.getHeight());
                            longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE);
                            imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE);
                            if (eqLongImage) {
                                // 加载长图
                                longImageView.setQuickScaleEnabled(true);
                                longImageView.setZoomEnabled(true);
                                longImageView.setDoubleTapZoomDuration(100);
                                longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
                                longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER);
                                longImageView.setImage(ImageSource.bitmap(resource),
                                        new ImageViewState(0, new PointF(0, 0), 0));
                            } else {
                                // 普通图片
                                imageView.setImageBitmap(resource);
                            }
                        }
                    }
                });
    }

    /**
     * 加载相册目录
     *
     * @param context   上下文
     * @param url       图片路径
     * @param imageView 承载图片ImageView
     */
    @Override
    public void loadFolderImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
        Glide.with(context)
                .asBitmap()
                .load(url)
                .override(180, 180)
                .centerCrop()
                .sizeMultiplier(0.5f)
                .placeholder(R.drawable.picture_image_placeholder)
                .into(new BitmapImageViewTarget(imageView) {
                    @Override
                    protected void setResource(Bitmap resource) {
                        RoundedBitmapDrawable circularBitmapDrawable =
                                RoundedBitmapDrawableFactory.
                                        create(context.getResources(), resource);
                        circularBitmapDrawable.setCornerRadius(8);
                        imageView.setImageDrawable(circularBitmapDrawable);
                    }
                });
    }


    /**
     * 加载gif
     *
     * @param context   上下文
     * @param url       图片路径
     * @param imageView 承载图片ImageView
     */
    @Override
    public void loadAsGifImage(@NonNull Context context, @NonNull String url,
                               @NonNull ImageView imageView) {
        Glide.with(context)
                .asGif()
                .load(url)
                .into(imageView);
    }

    /**
     * 加载图片列表图片
     *
     * @param context   上下文
     * @param url       图片路径
     * @param imageView 承载图片ImageView
     */
    @Override
    public void loadGridImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
        Glide.with(context)
                .load(url)
                .override(200, 200)
                .centerCrop()
                .placeholder(R.drawable.picture_image_placeholder)
                .into(imageView);
    }


    private GlideEngine() {
    }

    private static GlideEngine instance;

    public static GlideEngine createGlideEngine() {
        if (null == instance) {
            synchronized (GlideEngine.class) {
                if (null == instance) {
                    instance = new GlideEngine();
                }
            }
        }
        return instance;
    }
}

导入加载网络图片的依赖

 implementation 'com.github.bumptech.glide:glide:4.12.0'

MainActivity2.java中加上

点击事件读取相册

findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                PictureSelector.create(MainActivity2.this)
                        .openGallery(PictureMimeType.ofAll())
                        .imageEngine(GlideEngine.createGlideEngine())
                        .forResult(new OnResultCallbackListener<LocalMedia>() {
                            @Override
                            public void onResult(List<LocalMedia> result) {
                                // onResult Callback
                                for (LocalMedia localMedia : result){
                                    Log.i("MainActivity",localMedia.getPath());
                                }
                            }

                            @Override
                            public void onCancel() {
                                // onCancel Callback
                            }
                        });
            }
        });

我们点击相册中的图片通过回调函数可以拿到图片的本地路径(/storage/emulated/0/DCIM)

将获取到的图片显示到界面上面(读取到的流->图片,自己写是很麻烦的,我们使用Glide开源框架很简单就能完成)

这里我们只对所选择的第一张图片展示出来

Glide.with(MainActivity2.this).load(Uri.fromFile(new File(result.get(0).getRealPath()))).into(imageView);

显示多张图片在回调函数中通过列表控件RecyclerView循环

3.应用程序打包

将代码变成apk文件

在这里插入图片描述
在国内我们直接打包成apk文件,在国外发布到市场上面不是以apk的形式发布的。

在这里插入图片描述
第一次打包的时候需要生成一个钥匙,证明这个apk文件是你的,增加安全性。你有一个钥匙来证明。点击create
在这里插入图片描述
输入自己设置的两个密码,自己记住
在这里插入图片描述
对应用程序进行签名和加密
在这里插入图片描述
等待片刻…

这个路径下会生成apk文件
在这里插入图片描述
这个apk我们便可以安装在手机上了。但是如果需要很多人使用,一个一个的发送apk肯定是不行的,所以我们需要将app发布在应用市场上面。

那么如何将应用程序发布到市场上面?未完待续…

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a碟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值