Android进阶之绘制-自定义View完全掌握(一)

简介: Android进阶之绘制-自定义View完全掌握(一)

**Android的UI设计可以说是决定一个app质量的关键因素,因为人们在使用app的时候,最先映入眼帘的就是app的界面了,一个美观、充实的界面能够给用户带来非常好的体验,会在用户心中留下好的印象。
对于UI设计,Android原生的控件加上一些开源库的使用,已经能够满足大部分的UI需求,但是,某些比较新颖、花哨的控件效果,我们只能通过自定义View来实现,那么,从该篇博客开始,我将记录关于Android自定义View的学习内容,并将其整理呈现给大家。**
我们来实现一个优酷菜单案例,在案例中会涉及到很多方面的知识。
案例效果如下:
在这里插入图片描述
对activity_main.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="com.itcast.youkumenu.MainActivity">

    <RelativeLayout
        android:id="@+id/level1"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level1">
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/level2"
        android:layout_width="180dp"
        android:layout_height="90dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level2">
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/level3"
        android:layout_width="280dp"
        android:layout_height="140dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level3">
    </RelativeLayout>
    
</RelativeLayout>

预览一下效果。
在这里插入图片描述
貌似效果已经出来了,但是请注意了,我这样布局的话能点到每个圆环吗?看上面的图片,我只能点到蓝色线条框住的矩形,这是因为小圆环被大圆环覆盖了,我们重新修改一下布局代码。

<?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="com.itcast.youkumenu.MainActivity">

    <RelativeLayout
        android:id="@+id/level3"
        android:layout_width="280dp"
        android:layout_height="140dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level3">
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/level2"
        android:layout_width="180dp"
        android:layout_height="90dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level2">
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/level1"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level1">
    </RelativeLayout>

</RelativeLayout>

此时预览一下。
在这里插入图片描述
我已经能正确点击到每一个圆环,而我的代码只是将三个相对布局调换了一下位置,既然大圆环会覆盖到小圆环,那我们直接把大圆环放到最上面,这样就不会出现覆盖问题了。
这是我们需要注意的一个点。接下来贴出完整的布局代码。

<?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="com.itcast.youkumenu.MainActivity">

    <RelativeLayout
        android:id="@+id/level3"
        android:layout_width="280dp"
        android:layout_height="140dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level3">

        <ImageView
            android:id="@+id/channel1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="8dp"
            android:layout_marginLeft="8dp"
            android:src="@drawable/channel1" />

        <ImageView
            android:id="@+id/channel2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/channel1"
            android:layout_marginBottom="8dp"
            android:layout_marginLeft="33dp"
            android:src="@drawable/channel2" />

        <ImageView
            android:id="@+id/channel3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/channel2"
            android:layout_marginBottom="8dp"
            android:layout_marginLeft="63dp"
            android:src="@drawable/channel3" />

        <ImageView
            android:id="@+id/channel4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="8dp"
            android:src="@drawable/channel4" />

        <ImageView
            android:id="@+id/channel7"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="8dp"
            android:layout_marginRight="8dp"
            android:src="@drawable/channel7" />

        <ImageView
            android:id="@+id/channel6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/channel7"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="8dp"
            android:layout_marginRight="33dp"
            android:src="@drawable/channel6" />

        <ImageView
            android:id="@+id/channel5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/channel6"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="8dp"
            android:layout_marginRight="63dp"
            android:src="@drawable/channel5" />
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/level2"
        android:layout_width="180dp"
        android:layout_height="90dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level2">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="8dp"
            android:layout_marginLeft="8dp"
            android:src="@drawable/icon_search" />

        <ImageView
            android:id="@+id/icon_menu"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="8dp"
            android:src="@drawable/icon_menu" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="8dp"
            android:layout_marginRight="8dp"
            android:src="@drawable/icon_myyouku" />
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/level1"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level1">

        <ImageView
            android:id="@+id/icon_home"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/icon_home" />
    </RelativeLayout>

</RelativeLayout>

这样布局我们就完成了,接下来我们要实现功能了。
我们可以把功能分为两层,我们先完成中间圆环菜单键控制最外层圆环旋转的动画。然后再完成最里层圆环home键控制中间圆环旋转的动画。

修改MainActivity的代码。

package com.itcast.youkumenu;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private RelativeLayout level1;
    private RelativeLayout level2;
    private RelativeLayout level3;
    private ImageView icon_menu;
    private ImageView icon_home;

    /**
     * 是否显示最外层圆环
     * true:显示
     * false:隐藏
     */
    private boolean isShowLevel3 = true;

    /**
     * 是否显示中间圆环
     * true:显示
     * false:隐藏
     */
    private boolean isShowLevel2 = true;

    /**
     * 是否显示最里层圆环
     * true:显示
     * false:隐藏
     */
    private boolean isShowLevel1 = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();

    }

    private void initView() {
        level3 = (RelativeLayout) findViewById(R.id.level3);
        icon_menu = (ImageView) findViewById(R.id.icon_menu);
        level2 = (RelativeLayout) findViewById(R.id.level2);
        icon_home = (ImageView) findViewById(R.id.icon_home);
        level1 = (RelativeLayout) findViewById(R.id.level1);

        MyOnClickListener myOnClickListener = new MyOnClickListener();
        //设置点击事件
        icon_home.setOnClickListener(myOnClickListener);
        icon_menu.setOnClickListener(myOnClickListener);
    }

    class MyOnClickListener implements View.OnClickListener {

        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.icon_home://home键
                    //如果最外层菜单和中间菜单都是显示的,就都设置为隐藏
                    if(isShowLevel2){
                        //隐藏中间菜单
                        isShowLevel2 = false;
                        Tools.hideView(level2);

                        if(isShowLevel3){
                            //隐藏最外层菜单
                            isShowLevel3 = false;
                            Tools.hideView(level3,200);
                        }
                    }else{
                        //显示中间菜单
                        isShowLevel2 = true;
                        Tools.showView(level2);
                    }
                    //如果都是隐藏的,就仅显示中间菜单

                    break;
                case R.id.icon_menu://菜单键

                    if(isShowLevel3){
                        //隐藏
                        isShowLevel3 = false;
                        Tools.hideView(level3);
                    }else{
                        //显示
                        isShowLevel3 = true;
                        Tools.showView(level3);
                    }
                    break;
            }
        }
    }
}

在显示和隐藏布局的时候,我抽出了一个工具类用于实现旋转动画,工具类代码如下。

package com.itcast.youkumenu;

import android.view.View;
import android.view.animation.RotateAnimation;
import android.widget.RelativeLayout;

/**
 * 显示和隐藏指定的控件
 */
class Tools {

    public static void hideView(View view) {
        hideView(view,0);
    }

    public static void showView(View view) {
        RotateAnimation ra = new RotateAnimation(180, 360, view.getWidth() / 2, view.getHeight());
        ra.setDuration(500);//设置动画播放持续时间
        ra.setFillAfter(true);//动画停留在播放完成的状态
        view.startAnimation(ra);
    }

    public static void hideView(View view, int startOffset) {
        RotateAnimation ra = new RotateAnimation(0, 180, view.getWidth() / 2, view.getHeight());
        ra.setDuration(500);//设置动画播放持续时间
        ra.setFillAfter(true);//动画停留在播放完成的状态
        ra.setStartOffset(startOffset);//设置动画延迟时间
        view.startAnimation(ra);
    }
}

运行项目,效果如下。
在这里插入图片描述
现在我们要实现一下点击menu键也能隐藏圆环。要想实现这个效果,就得对手机按钮进行控制。
重新修改MainActivity的代码。

package com.itcast.youkumenu;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.KeyEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private RelativeLayout level1;
    private RelativeLayout level2;
    private RelativeLayout level3;
    private ImageView icon_menu;
    private ImageView icon_home;

    /**
     * 是否显示最外层圆环
     * true:显示
     * false:隐藏
     */
    private boolean isShowLevel3 = true;

    /**
     * 是否显示中间圆环
     * true:显示
     * false:隐藏
     */
    private boolean isShowLevel2 = true;

    /**
     * 是否显示最里层圆环
     * true:显示
     * false:隐藏
     */
    private boolean isShowLevel1 = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();

    }

    private void initView() {
        level3 = (RelativeLayout) findViewById(R.id.level3);
        icon_menu = (ImageView) findViewById(R.id.icon_menu);
        level2 = (RelativeLayout) findViewById(R.id.level2);
        icon_home = (ImageView) findViewById(R.id.icon_home);
        level1 = (RelativeLayout) findViewById(R.id.level1);

        MyOnClickListener myOnClickListener = new MyOnClickListener();
        //设置点击事件
        icon_home.setOnClickListener(myOnClickListener);
        icon_menu.setOnClickListener(myOnClickListener);
    }

    class MyOnClickListener implements View.OnClickListener {

        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.icon_home://home键
                    //如果最外层菜单和中间菜单都是显示的,就都设置为隐藏
                    if(isShowLevel2){
                        //隐藏中间菜单
                        isShowLevel2 = false;
                        Tools.hideView(level2);

                        if(isShowLevel3){
                            //隐藏最外层菜单
                            isShowLevel3 = false;
                            Tools.hideView(level3,200);
                        }
                    }else{
                        //显示中间菜单
                        isShowLevel2 = true;
                        Tools.showView(level2);
                    }
                    //如果都是隐藏的,就仅显示中间菜单

                    break;
                case R.id.icon_menu://菜单键

                    if(isShowLevel3){
                        //隐藏
                        isShowLevel3 = false;
                        Tools.hideView(level3);
                    }else{
                        //显示
                        isShowLevel3 = true;
                        Tools.showView(level3);
                    }
                    break;
            }
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_MENU){

            //如果三个圆环是显示的,就全部隐藏
            if (isShowLevel1){
                isShowLevel1 = false;
                Tools.hideView(level1);
                if(isShowLevel2){
                    //隐藏中间圆环
                    isShowLevel2 = false;
                    Tools.hideView(level2,200);

                    if (isShowLevel3){
                        //隐藏最外层圆环
                        isShowLevel3 = false;
                        Tools.hideView(level3,400);
                    }
                }
            }else{
                //如果最里层圆环和中间圆环是隐藏的,就显示
                //显示最里层圆环
                isShowLevel1 = true;
                Tools.showView(level1);

                //显示中间圆环
                isShowLevel2 = true;
                Tools.showView(level2,200);
            }

            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
}

工具类代码。

package com.itcast.youkumenu;

import android.view.View;
import android.view.animation.RotateAnimation;
import android.widget.RelativeLayout;

/**
 * 显示和隐藏指定的控件
 */
class Tools {

    public static void hideView(View view) {
        hideView(view,0);
    }

    public static void showView(View view) {
        showView(view,0);
    }

    public static void hideView(View view, int startOffset) {
        RotateAnimation ra = new RotateAnimation(0, 180, view.getWidth() / 2, view.getHeight());
        ra.setDuration(500);//设置动画播放持续时间
        ra.setFillAfter(true);//动画停留在播放完成的状态
        ra.setStartOffset(startOffset);//设置动画延迟时间
        view.startAnimation(ra);
    }

    public static void showView(View view, int startOffset) {
        RotateAnimation ra = new RotateAnimation(180, 360, view.getWidth() / 2, view.getHeight());
        ra.setDuration(500);//设置动画播放持续时间
        ra.setFillAfter(true);//动画停留在播放完成的状态
        ra.setStartOffset(startOffset);
        view.startAnimation(ra);
    }
}

运行项目,效果如下。
在这里插入图片描述
这样就实现了再点击手机的menu键时圆环旋转消失,但是这样就产生了一个bug,不知道观察了上面的动图大家发现bug没有,当我按menu键隐藏圆环时,我再去点击圆环的位置,圆环还是旋转出来了,按道理我们的圆环消失后,就不能被我们点击出来了吧。这里就涉及到了普通动画和属性动画的区别了。当然,解决办法有很多,我这里介绍两种。
第一种,给每个孩子设置不可以点击。
那很多人就有点子了,可以在Tools类的hideView()方法中添加view.setEnabled(false);,然后在showView()方法中添加view.setEnabled(true);,有些人以为这样就能够解决bug了。其实以为这样可以解决问题的人,他就不了解View和ViewGroup的区别,View是不能够对孩子进行操作的,而我们在方法中将传递过来的布局转换为了View,它原先的某些属性就丢失了。其实,对view参数设置了不可点击的话,相对布局确实变得无法被点击了,但是,它的孩子还是可以被点击的。那应该怎么办呢?我们把Tools类中的四个方法的View参数全部改为ViewGroup,然后对ViewGroup的孩子进行禁止点击的操作。具体代码如下。

package com.itcast.youkumenu;

import android.view.View;
import android.view.ViewGroup;
import android.view.animation.RotateAnimation;
import android.widget.RelativeLayout;

/**
 * 显示和隐藏指定的控件
 */
class Tools {

    public static void hideView(ViewGroup view) {
        hideView(view,0);
    }

    public static void showView(ViewGroup view) {
        showView(view,0);
    }

    public static void hideView(ViewGroup view, int startOffset) {
        RotateAnimation ra = new RotateAnimation(0, 180, view.getWidth() / 2, view.getHeight());
        ra.setDuration(500);//设置动画播放持续时间
        ra.setFillAfter(true);//动画停留在播放完成的状态
        ra.setStartOffset(startOffset);//设置动画延迟时间
        view.startAnimation(ra);

        for(int i = 0;i < view.getChildCount();i++){
            View childView = view.getChildAt(i);
            //设置不可以点击
            childView.setEnabled(false);
        }
    }

    public static void showView(ViewGroup view, int startOffset) {
        RotateAnimation ra = new RotateAnimation(180, 360, view.getWidth() / 2, view.getHeight());
        ra.setDuration(500);//设置动画播放持续时间
        ra.setFillAfter(true);//动画停留在播放完成的状态
        ra.setStartOffset(startOffset);
        view.startAnimation(ra);

        for(int i = 0;i < view.getChildCount();i++){
            View childView = view.getChildAt(i);
            //设置不可以点击
            childView.setEnabled(true);
        }
    }
}

这时你再运行项目,然后点击menu键隐藏圆环后,不管你怎么点击,圆环都不会再出来了。

第二种方法,前面也说到了,我们可以通过属性动画解决该bug。
属性动画和普通动画的区别在于,普通动画只有视觉效果,而控件不会改变它的位置;属性动画不仅有动画效果,而且控件会随着动画而改变位置。可以想象,使用属性动画来旋转的话,当动画执行完毕时,布局旋转180度,此时控件都会旋转到屏幕的下方,这样,用户就点击不到控件从而也就不能触发点击事件了。
修改工具类代码。

package com.itcast.youkumenu;

import android.animation.ObjectAnimator;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.RotateAnimation;
import android.widget.RelativeLayout;

/**
 * 显示和隐藏指定的控件
 */
class Tools {

    public static void hideView(ViewGroup view) {
        hideView(view, 0);
    }

    public static void showView(ViewGroup view) {
        showView(view, 0);
    }

    public static void hideView(ViewGroup view, int startOffset) {
//        RotateAnimation ra = new RotateAnimation(0, 180, view.getWidth() / 2, view.getHeight());
//        ra.setDuration(500);//设置动画播放持续时间
//        ra.setFillAfter(true);//动画停留在播放完成的状态
//        ra.setStartOffset(startOffset);//设置动画延迟时间
//        view.startAnimation(ra);
//
//        for(int i = 0;i < view.getChildCount();i++){
//            View childView = view.getChildAt(i);
//            //设置不可以点击
//            childView.setEnabled(false);
//        }

        //改为属性动画
        ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotation", 0, 180);
        animator.setDuration(500);
        animator.setStartDelay(startOffset);
        animator.start();
        view.setPivotX(view.getWidth() / 2);
        view.setPivotY(view.getHeight());
    }

    public static void showView(ViewGroup view, int startOffset) {
//        RotateAnimation ra = new RotateAnimation(180, 360, view.getWidth() / 2, view.getHeight());
//        ra.setDuration(500);//设置动画播放持续时间
//        ra.setFillAfter(true);//动画停留在播放完成的状态
//        ra.setStartOffset(startOffset);
//        view.startAnimation(ra);
//
//        for (int i = 0; i < view.getChildCount(); i++) {
//            View childView = view.getChildAt(i);
//            //设置不可以点击
//            childView.setEnabled(true);
//        }

        //改为属性动画
        ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotation", 180, 360);
        animator.setDuration(500);
        animator.setStartDelay(startOffset);
        animator.start();
        view.setPivotX(view.getWidth() / 2);
        view.setPivotY(view.getHeight());
    }
}

运行项目,效果和原来相同,但是bug却已经解决了,大家可以自己试一试,原理我也已经说过了。对于动画,Android中分了三个部分,补间动画、帧动画、属性动画,动画的话,我也会在今后的博客中专门讲解一下。那么今天的内容就到这里了。

点击下载源码

目录
相关文章
|
4天前
|
API Android开发 开发者
Android经典实战之使用ViewCompat来处理View兼容性问题
本文介绍Android中的`ViewCompat`工具类,它是AndroidX库核心部分的重要兼容性组件,确保在不同Android版本间处理视图的一致性。文章列举了设置透明度、旋转、缩放、平移等功能,并提供了背景色、动画及用户交互等实用示例。通过`ViewCompat`,开发者可轻松实现跨版本视图操作,增强应用兼容性。
24 5
|
19天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
39 2
|
1月前
|
Android开发
Android面试题之自定义View注意事项
在Android开发中,自定义View主要分为四类:直接继承View重写onDraw,继承ViewGroup创建布局,扩展特定View如TextView,以及继承特定ViewGroup。实现时需注意:支持wrap_content通过onMeasure处理,支持padding需在onDraw或onMeasure/onLayout中处理。避免在View中使用Handler,使用post系列方法代替。记得在onDetachedFromWindow时停止线程和动画以防止内存泄漏。处理滑动嵌套时解决滑动冲突,并避免在onDraw中大量创建临时对象。
24 4
|
1月前
|
Android开发
Android面试题之View的invalidate方法和postInvalidate方法有什么区别
本文探讨了Android自定义View中`invalidate()`和`postInvalidate()`的区别。`invalidate()`在UI线程中刷新View,而`postInvalidate()`用于非UI线程,通过消息机制切换到UI线程执行`invalidate()`。源码分析显示,`postInvalidate()`最终调用`ViewRootImpl`的`dispatchInvalidateDelayed`,通过Handler发送消息到UI线程执行刷新。
27 1
|
20天前
|
机器学习/深度学习 人工智能 算法
探索AI在医疗影像分析中的应用探索安卓开发中的自定义View组件
【7月更文挑战第31天】随着人工智能技术的飞速发展,其在医疗健康领域的应用日益广泛。本文将聚焦于AI技术在医疗影像分析中的运用,探讨其如何通过深度学习模型提高诊断的准确性和效率。我们将介绍一些关键的深度学习算法,并通过实际代码示例展示这些算法是如何应用于医学影像的处理和分析中。文章旨在为读者提供对AI在医疗领域应用的深刻理解和实用知识。
22 0
|
1月前
|
前端开发 API Android开发
Android自定义View之Canvas一文搞定
这篇文章介绍了Android自定义View中如何使用Canvas和Paint来绘制图形。Canvas可理解为画布,用于绘制各种形状如文字、点、线、矩形、圆角矩形、圆和弧。常见API包括`drawText()`、`drawPoint()`、`drawLine()`、`drawRect()`等。文章还提到了Canvas的保存、恢复、平移和旋转方法,通过绘制钟表盘的例子展示了如何实际应用。总结关键点:Canvas与Paint结合用于图像绘制,掌握Canvas的基本绘图函数及坐标变换操作是自定义View的关键。
23 0
Android自定义View之Canvas一文搞定
|
27天前
|
消息中间件 调度 Android开发
Android经典面试题之View的post方法和Handler的post方法有什么区别?
本文对比了Android开发中`View.post`与`Handler.post`的使用。`View.post`将任务加入视图关联的消息队列,在视图布局后执行,适合视图操作。`Handler.post`更通用,可调度至特定Handler的线程,不仅限于视图任务。选择方法取决于具体需求和上下文。
27 0
|
2天前
|
JavaScript 前端开发 Java
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
IT寒冬使APP开发门槛提升,安卓程序员需转型。选项包括:深化Android开发,跟进Google新技术如Kotlin、Jetpack、Flutter及Compose;研究Android底层框架,掌握AOSP;转型Java后端开发,学习Spring Boot等框架;拓展大前端技能,掌握JavaScript、Node.js、Vue.js及特定框架如微信小程序、HarmonyOS;或转向C/C++底层开发,通过音视频项目如FFmpeg积累经验。每条路径都有相应的书籍和技术栈推荐,助你顺利过渡。
13 3
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向