• 大学android开发基础课程,代码齐全,移动开发项目化教程(双色)》(李扬)。
  • 丛编项: 21世纪高等院校移动开发人才培养规划教材 本书通过精心设计的7个工作项目,全程贯彻“做中学”理念,先实践认知,后理论拓展,由浅入深,让读者逐步掌握Android应用程序用户界面布局设计、2D绘图和游戏...
  • Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发
  • 直接去出版社官网下载就好,简单有效,亲自经历,避免踩已踩过的坑

    直接去出版社官网下载就好,简单有效,亲自经历,避免踩已踩过的坑

    展开全文
  • 参考书目:《Android移动开发基础案例教程》 出版社:中国工信出版集团丨人民邮电出版社 编著:黑马程序员 --------------- 很认真写的期末考试总结,供大家参考! · 移动通信1G、2G、3G、4G的概念、优缺点...

     

    参考书目:《Android移动开发基础案例教程》 

    出版社:中国工信出版集团丨人民邮电出版社

    编著:黑马程序员

    ---------------

    很认真写的期末考试总结,供大家参考!

     

    · 移动通信1G、2G、3G、4G的概念、优缺点

    答:1G是模拟信号,支持语音通话,安全性差,容易被窃听,代表产品:大哥大;2G采用数字信号传输,信号强,支持收发短信,支持小的Java程序,支持数据业务,GPRS工作在2.5GHz,EDGE工作在2.75GHz;3G主要技术有TDSCDMA(中国移动)、WCDMA(中国联通)和CDMA2000(中国电信),HSDPA工作在3.5GHz,提供较快的数据业务;4G是指第四代移动通信技术,主要技术有TD-LTE(中国移动、中国联通、中国电信)和FDD-LTE(中国联通、中国电信),提供更快的数据业务,通过VoLTE技术,支持语音通话在4G频段上进行,提供高清视频、语音通话。

    · Android体系结构由几层构成?分别是哪几层?简单介绍每一层的概念、应用

    答:四层,分别是应用程序层(Applications)、应用程序框架层(Application Framework)、核心类库层(Libraries)以及Linux内核层(Linux Kernel)。应用程序层是应用程序的集合,各种安装在手机上的App都属于这一层;应用程序框架层主要提供了/构建应用程序时/用到的各种API接口,Android自带的一些核心应用就是使用这些API完成的,例如视图(View)、活动管理器(Activity Manager)、通知管理器(Notification Manager)等;核心类库层包含了系统库/及Android运行时库,系统库主要是通过C/C++库来为Android系统提供主要的特性支持,如OpenGL/ES库提供了3D绘图的支持,Android运行时库提供了Dalvik虚拟机(安卓5.0开始彻底更换为ART);Linux内核层为Android设备的各种硬件提供了底层的驱动,如显示驱动、音频驱动等。

    · Drawable、Layout、AndroidManifest.xml分别存放什么内容?

    答:src目录是源代码目录,存放的是.java文件;gen目录存放ADT自动生成的java文件;bin目录保存了编译过程中产生的文件,以及最终的apk文件;res是资源目录,其中,drawable目录用来保存同一个程序中针对不同屏幕尺寸需要显示的不同大小的图像文件,layout目录用来保存与用户界面相关的布局文件,values目录保存颜色、风格、主题和字符串等资源;project.properties文件记录了Android工程的相关设置;AndroidManifest.xml是XML格式的Android程序声明文件,包含了Android系统运行Android程序前所必须掌握的重要信息,这些信息包括应用程序名称、icon、包名称、模块组成、授权和SDK最低版本等,而且每个Android程序必须在根目录下包含一个AndroidManifest.xml文件。

    · Android的常用布局有几种?分别简单介绍它们的特点

    答:分别有LinearLayout(线性布局)、RelativeLayout(相对布局)、FrameLayout(帧/框架布局)、TableLayout(表格布局)、GridLayout(网格布局)和AbsoluteLayout(绝对布局)六种。线性布局(LinearLayout)主要以水平和垂直方式来显示界面中的控件;相对布局(RelativeLayout)是通过相对定位的方式指定控件位置,后放置的控件依赖于先放置的控件,是Android默认的布局;帧布局(FrameLayout)是Android中最简单的布局,该布局为每个加入其中的控件创建一个空白区域,所有控件都默认显示在屏幕左上角,并按照先后放入的次序重叠排放,先放入的控件显示在最底层,后放入的控件显示在最顶层;表格布局(TableLayout)是以表格形式排列控件的,通过行和列将界面划分为多个单元格,每个单元格都可以添加控件;网格布局(GridLayout)是Android 4.0新支持的布局方式,将用户界面划分为网格,界面元素可随意摆放在这些网格中;绝对布局(AbsoluteLayout)是通过指定x、y坐标来控制每一个控件位置的。

    · TextView和Button的常用属性有哪些?

     

    控件属性

    功能描述

    TextView

    android:text

    设置显示文本

    android:textColor

    设置文本的颜色

    android:textSize

    设置文字大小,推荐单位为sp,如android:textSize = “15sp”

    android:textStyle

    设置文字样式,如bold(粗体),italic(斜体),bolditalic(粗斜体)

    android:height

    设置文本区域的高度,推荐单位为dp

    android:width

    设置文本区域的宽度,推荐单位为dp

    android:maxLength

    设置文本长度,超出不显示,如android:maxLength = “10”

    android:password

    设置文本以密码形式“.”显示

    android:gravity

    设置文本位置,如设置成”center”,文本将居中显示

    android:phoneNumber

    设置以电话号码的方式输入

    android:layout_height

    设置TextView控件的高度

    android:layout_width

    设置TextView控件的宽度

     

    控件属性

    功能描述

    Button

    android:text

    设置显示文本

    android:layout_height

    设置Button控件的高度

    android:layout_width

    设置Button控件的宽度

    android:layout_weight

    设置Button控件的权重

    android:onClick

    点击此控件时调用的方法

    android:drawable

    在Button组件上放置图片

    · Android的四大组件是什么?它们的作用分别是什么?

    答:Activity(界面)为用户提供可视化界面及操作;Service(服务)是一个长期运行在后台的用户组件,适用于开发没有用户界面且长时间在后台运行的应用功能;Content Provider(数据交换/内容提供者)是在不同程序之间实现数据共享;Broadcast Receiver(广播/广播接收者)是监听系统中的广播消息,以实现在不同组件之间的通信。

    · Activity的生命周期有几种状态?有几种方法?什么时候调用这些方法?作用是什么?

    答:Activity的生命周期分为5种状态,分别是启动状态、运行状态、暂停状态、停止状态和销毁状态。启动状态很短暂,一般当Activity启动之后便会进入运行状态;运行状态时Activity处于屏幕最前端,它是可见、有焦点的,可以与用户进行交互;暂停状态是当Activity在界面上被部分遮挡,且不能与用户进行交互,如锁屏;停止状态是当Activity在界面上被完全遮挡,完全不能被用户看到,当系统内存不足时,很容易被销毁;销毁状态时Activity将被kill,清理出内存。

    Activity的生命周期中主要涉及7种方法(事件回调函数),分别是onCreate()、onStart()、onResume、onPause()、onStop()、onDestroy()和onRestart()方法/函数。onCreate()方法是在Activity创建时调用,通常做一些初始化设置;onStart()方法在Activity即将可见时调用;onResume()方法在Activity可以接收用户输入时调用,此时的Activity位于Activity栈的栈顶;onPause()方法在进入暂停状态时被调用,主要用来保存持久数据、关闭动画、释放CPU资源等;onStop()方法在Activity不对用户可见时调用,Activity进入停止状态;onDestroy()方法在Activity销毁时调用;onRestart()方法在Activity从停止状态进入活动状态前调用。

    · Activity之间的跳转需要用到(       )组件,根据开启目标组件的方式不同,Intent被分为哪两种类型?简单介绍这两种类型

    答:Activity之间的跳转需要用到Intent组件,Intent是一种轻量级的消息传递机制,可以在同一个应用程序内部的不同组件之间传递信息,根据开启目标组件的方式不同,Intent被分为显式启动(意图)和隐式启动(意图)两种类型,显式启动可以通过名称开启指定的目标组件;隐式启动不需要指明需要启动哪一个Activity,而是通过指定action和category等属性信息,由Android系统来决定。

    · 对于Android平台提供的5种数据存储方式,简单介绍它们

    文件存储

    分为内部存储和外部存储,直接使用Android文件系统存储数据,可以存储体积较大的数据;

     

    SharePreferences

    它是Android提供的用来存储一些简单的配置信息的一种机制,采用了.xml格式将数据存储到设备中,可以存储应用程序的各种配置信息,如用户名、密码等;

     

    ContentProvider

    主要用于应用程序之间的数据交换,它可以将自己的数据共享给其他应用程序使用;

     

    SQLite数据库

    Android自带的一个轻量级数据库,支持基本SQL语法,利用很少的内存就有很好的性能,一般使用它作为复杂数据的存储引擎,可以存储用户信息等;

     

    网络存储

    将数据存储到服务器上,通过网络提供的存储空间来存储、获取数据信息。

     

     

    · 【程序改错题、程序填空题】

    package com.example.saveqq;  
      
    import java.util.HashMap;  
    import java.util.Map;  
      
    import android.content.Context;  
    import android.content.SharedPreferences;  
      
    public class SPSaveQQ{  
        // 保存QQ账号和登录密码到data.xml文件中  
        public static boolean saveUserInfo(Context context, String number, String password) {  
            SharedPreferences sp = context.getSharedPreferences("data", Context.MODE_PRIVATE);  
            SharedPreferences.Editor edit = sp.edit();  
            edit.putString("userName", number);  
            edit.putString("pwd", password);  
            edit.commit();  
            return true;  
        }  
        //从data.xml文件中获取存储的QQ账号和密码  
        public static Map<String, String> getUserInfo(Context context) {  
            SharedPreferences sp = context.getSharedPreferences("data", Context.MODE_PRIVATE);  
            String number = sp.getString("userName", null);  
            String password = sp.getString("pwd", null);  
            Map<String, String> userMap = new HashMap<String, String>();  
            userMap.put("number", number);  
            userMap.put("password", password);  
            return userMap;  
        }  
    }  

     

    · 服务的启动方式有几种?分别是哪几种?

    答:两种。分别是startService方式启动和bindService方式启动,通过startService()方法启动服务,服务会长期在后台运行,并且服务的状态与开启者的状态没有关系,即使启动服务的组件已经被销毁,服务也会依旧运行;当一个组件通过bindService()启动服务时,服务会与组件绑定,一个被绑定的服务提供一个客户端与服务器接口,允许组件与服务交互、发送请求、得到结果,多个组件可以绑定一个服务,当调用onUnbind()方法时,这个服务就会被销毁。

    · 【程序改错题、程序填空题】

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        xmlns:tools="http://schemas.android.com/tools"  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        android:background="@drawable/bg"  
        android:orientation="vertical">  
      
        <EditText  
            android:id="@+id/et_inputpath"  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:text="data/data/com.example.musicplayer/a.mp3" />  
        <LinearLayout  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:layout_marginTop="10dp"  
            android:layout_gravity="center_horizontal"  
            android:gravity="center"  
            android:orientation="horizontal" >  
            <TextView  
                 android:id="@+id/tv_play"  
                 android:layout_width="0dp"  
                 android:layout_height="wrap_content"  
                 android:layout_weight="1"  
                 android:drawablePadding="3dp"  
                 android:drawableTop="@drawable/play"  
                 android:gravity="center"  
                 android:text="播放" />  
            <TextView  
                 android:id="@+id/tv_pause"  
                 android:layout_width="0dp"  
                 android:layout_height="wrap_content"  
                 android:layout_weight="1"  
                 android:drawablePadding="3dp"  
                 android:drawableTop="@drawable/pause"  
                 android:gravity="center"  
                 android:text="暂停" />  
        </LinearLayout>  
    </LinearLayout>  

    · 【程序改错题、程序填空题】

    package com.example.readsms;  
      
    import java.util.ArrayList;  
    import java.util.List;  
      
    import android.app.Activity;  
    import android.content.ContentResolver;  
    import android.database.Cursor;  
    import android.net.Uri;  
    import android.os.Bundle;  
    import android.view.View;  
    import android.widget.TextView;  
      
    public class MainActivity extends Activity {  
        private TextView tvSms;  
        private TextView tvDes;  
        private String text = "";  
        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            // TODO Auto-generated method stub  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_main);  
            tvSms = (TextView) findViewById(R.id.tv_sms);  
            tvDes = (TextView) findViewById(R.id.tv_des);  
        }  
        public void readSMS(View view) {  
            Uri uri = Uri.parse("content://sms/");  
            ContentResolver resolver = getContentResolver();  
            Cursor cursor = resolver.query(uri, new String[]{ "_id", "address",   
                    "type", "body","date"}, null, null, null);  
            List<SmsInfo> smsInfos = new ArrayList<SmsInfo>();  
            if(cursor != null && cursor.getCount() >0 ) {  
                tvDes.setVisibility(View.VISIBLE);  
                while(cursor.moveToNext()) {  
                    int _id = cursor.getInt(0);  
                    String address = cursor.getString(1);  
                    int type = cursor.getInt(2);  
                    String body = cursor.getString(1);  
                    long date = cursor.getLong(4);  
                    SmsInfo smsInfo = new SmsInfo(_id, address, type, body, date);  
                    smsInfos.add(smsInfo);  
                }  
                cursor.close();  
            }  
            for (int i = 0;i < smsInfos.size(); i++) {  
                text += "手机号码:" + smsInfos.get(i).getAddress() + "\n";  
                text += "短信内容:" + smsInfos.get(i).getBody() + "\n\n";  
                tvSms.setText(text);  
            }  
        }  
          
    }  

    · 【程序改错题、程序填空题】

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
       xmlns:tools="http://schemas.android.com/tools"  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        android:background="@android:color/white"  
        tools:context=".MainActivity">  
        <ImageView  
            android:id="@+id/iv_flower"  
            android:layout_width="150dp"  
            android:layout_height="267dp"  
            android:layout_centerInParent="true"  
            android:background="@layout/frame"  
            android:layout_marginBottom="20dp" />  
        <Button  
            android:id="@+id/btn_play"  
            android:layout_width="70dp"  
            android:layout_height="70dp"  
            android:layout_centerInParent="true"  
            android:background="@android:drawable/ic_media_play"/>  
    </RelativeLayout>  

    · 【程序改错题、程序填空题】

    package com.example.frame;  
      
    import android.app.Activity;  
    import android.graphics.drawable.AnimationDrawable;  
    import android.os.Bundle;  
    import android.view.View;  
    import android.widget.Button;  
    import android.widget.ImageView;  
      
    public class MainActivity extends Activity implements View.OnClickListener{  
        private ImageView iv_flower;  
        private Button btn_start;  
        private AnimationDrawable animation;   
        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_main);  
            iv_flower = (ImageView) findViewById(R.id.iv_flower);  
            btn_start = (Button) findViewById(R.id.btn_play);  
            btn_start.setOnClickListener(this);  
            //拿到AnimationDrawable对象  
             animation = (AnimationDrawable)iv_flower.getBackground();         
        }  
      
        public void onClick(View v) {  
            //播放动画  
            if(!animation.isRunning()){  
                animation.start();  
                btn_start.setBackgroundResource(android.R.drawable.ic_media_pause);  
            }else{  
                animation.stop();  
                btn_start.setBackgroundResource(android.R.drawable.ic_media_play);  
            }  
        }  
    }  

    · 【程序改错题、程序填空题】

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        xmlns:tools="http://schemas.android.com/tools"  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        android:orientation="vertical"  
        tools:context=".MainActivity" >  
      
        <RelativeLayout  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content" >  
      
            <EditText  
                android:id="@+id/et_path"  
                android:layout_width="match_parent"  
                android:layout_height="wrap_content"  
                android:hint="请输入视频文件的路径"  
                android:layout_toLeftOf="@+id/bt_play" />  
      
            <ImageView  
                android:id="@+id/bt_play"  
                android:layout_width="wrap_content"  
                android:layout_height="wrap_content"  
                android:layout_alignParentRight="true"  
                android:layout_centerVertical="true"  
                android:src="@android:drawable/ic_media_play" />  
        </RelativeLayout>  
      
        <LinearLayout  
            android:layout_width="wrap_content"  
            android:layout_height="match_parent"  
            android:layout_below="@+id/play" >  
      
            <VideoView  
                android:id="@+id/video_view"  
                android:layout_width="match_parent"  
                android:layout_height="match_parent" />  
        </LinearLayout>  
      
    </LinearLayout>  

    · 【程序改错题、程序填空题】

    package com.example.videoview;  
      
    import android.app.Activity;  
    import android.media.MediaPlayer;  
    import android.os.Bundle;  
    import android.view.View;  
    import android.widget.EditText;  
    import android.widget.ImageView;  
    import android.widget.MediaController;  
    import android.widget.VideoView;  
    public class MainActivity extends Activity implements View.OnClickListener {  
        private EditText et_path;  
        private ImageView bt_play;  
        private VideoView videoView;  
        private MediaController controller;  
        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_main);  
            et_path = (EditText) findViewById(R.id.et_path);  
            bt_play = (ImageView) findViewById(R.id.bt_play);  
            videoView = (VideoView) findViewById(R.id.video_view);  
            controller = new MediaController(this);  
            videoView.setMediaController(controller);  
            bt_play.setOnClickListener(this);  
        }  
        @Override  
        public void onClick(View v) {  
            switch (v.getId()) {  
            case R.id.bt_play:  
                play();  
                break;  
            }  
        }  
        private void play() {  
            if (videoView != null && videoView.isPlaying()) {  
                bt_play.setImageResource(android.R.drawable.ic_media_play);  
                videoView.stopPlayback();  
                return;  
            }  
            videoView.setVideoPath(et_path.getText().toString());  
            videoView.start();  
            bt_play.setImageResource(android.R.drawable.ic_media_pause);  
            videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {  
                @Override  
                public void onCompletion(MediaPlayer mp) {  
                    // TODO Auto-generated method stub  
                    bt_play.setImageResource(android.R.drawable.ic_media_play);  
                }  
            });  
        }  
    }  

     

    END.

    展开全文
  • 这个是工业和信息“十三五”人才培养规划教材Android移动开发基础案例教程,黑马程序员编著配套源代码
  • 经过几天的思考已完美解决所有问题,下面就和大家分享下这个摇杆的开发思路(此教程不包含游戏源码) 若有不正之处,请多多谅解并欢迎指正。 首先这个摇杆要用到较多的数学知识,小编的数学特别烂也就高中水平吧 ...

    李子果 原创。。。

    最近在做一个山寨版的王者荣耀,刚开始做的时候毫无头绪 摇杆的多点触控做的特别烂

    经过几天的思考已完美解决所有问题,下面就和大家分享下这个摇杆的开发思路(此教程不包含游戏源码)

    若有不正之处,请多多谅解并欢迎指正。

    首先这个摇杆要用到较多的数学知识,小编的数学特别烂也就高中水平吧

    我们这个摇杆一共就五个按钮,一个移动摇杆、三个技能摇杆和一个普通攻击按钮

    最终效果

    好了废话少说让我们开始吧

    新建一个项目

    建好项目之后,我们先新建一个类叫做“画”。也是我们的主View

    修改Hua.java的代码

    public class Hua extends RelativeLayout implements Runnable{ //继承RelativeLayout 实现Runnable接口
    
        private Paint p;//画笔
    
        public Hua(Context context) {
            super(context);
            p=new Paint();
            setBackgroundColor(Color.BLACK);//背景颜色设为黑色
        }
    
        @Override
        protected void onDraw(Canvas g) {//重写onDraw方法
            super.onDraw(g);
        }
    
        @Override
        public void run() {
    
        }
    }

    接下来我们做移动摇杆

     

    我们要准备一张图片(待会我会把图片都丢在附件里)

     

    首先用ps画一个这样半透明的圆ps部分就不做教程了

    把背景隐藏掉 保存为png格式

     

    把我们刚刚制作的图片添加进来

    先新建一个类 my.java

    public class my { //这个类当一个全局变量使用
        public static int w,h;//屏幕的宽高
        public static float bili;
        public static MainActivity main;
        public static RectF re=new RectF();
        public static int ontouchAlpha=100;//触控区透明度0-255 0为透明,为了测试我们先设为100
    }

    修改 MainActivity 的代码

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            my.main=this;
            getSupportActionBar().hide();//隐藏标题栏
            this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏 隐藏状态栏
            //判断当前是否横屏 如果不是就设为横屏,设为横屏之后会自动调用onCreate方法
            if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                //获取屏幕的宽高
                DisplayMetrics dis = getResources().getDisplayMetrics();
                my.w = dis.widthPixels;
                my.h = dis.heightPixels;
                //获取屏幕分辨率和1920*1080的比例 以便适应不同大小的屏幕
                my.bili = (float) (Math.sqrt(my.w * my.h) / Math.sqrt(1920 * 1080));
                setContentView(new Hua(this));
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);// 横屏
            }
        }
    }

    新建类 Move.java

    package com.yaogan.liziguo.yaogan;
    
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    
    /**
     * Created by Liziguo on 2018/6/15.
     */
    
    public class Move {
        private float x1,y1;//按下时的坐标 大圆
        private float x2,y2;//移动后的坐标 小圆
        private final float r1,r2;//r1大圆的半径 r2小圆的半径
        public float angle;//x1y1指向x2y2的角度 弧度制
        public boolean down=false;//判断是否被按下
        public boolean in=false;//判断小圆是否在大圆里面,简单的说就是防止小圆被脱太远
        public boolean move=false;//判断手指按下后是否移动(MY实际开发中用到,该教程用不到此变量)
        public Bitmap img;//大圆小圆的图片
    
        public Move(){
            r1 = 480 * 0.5f * my.bili;//乘上一个比例 适应不同大小的屏幕
            r2 = 300 * 0.5f * my.bili;
            img= BitmapFactory.decodeResource(my.main.getResources(),R.mipmap.yaogan);//初始化摇杆图片
        }
    
        public void down(float xx,float yy){ //摇杆按下后的操作
            if(xx<r1) x1=r1;
            else x1 = xx;
    
            if(my.h-yy<r1) y1=my.h-r1;
            else y1 = yy;
            //上面的代码是防止按下的位置太靠近屏幕边缘
            //跟x1=xx;y1=yy;区别不大,待会可以改成x1=xx;y1=yy;看看效果有什么不同
            down=true;
        }
        public void move(float xx,float yy){ //按下摇杆后移动的操作
            angle=getAngle(xx,yy);
            in=in(xx, yy);
            move=isMove(xx,yy);
            if (!in) {
                //下面会做解释
                xx= (float) (x1+ Math.sin(angle)*r1*0.7f);
                yy= (float) (y1+ Math.cos(angle)*r1*0.7f);
            }
            x2=xx;
            y2=yy;
        }
        public void up(){ //松开后的操作
            down=false;
        }
    
        public float getAngle(float xx,float yy){ //获取x1y1指向x2y2的角度
            double angle,k;
            if (y1==yy)//斜率不存在时
                if (x1 > xx)//判断x1指向x2的方向
                    angle=-Math.PI/2;
                else
                    angle=Math.PI/2;
            else{
                k=(x1-xx)/(y1-yy); //两点的坐标求斜率,至于为什么是(x1-x2)/(y1-y2)不是(y1-y2)/(x1-x2)待会我们再做解释
                if (y1 > yy) {//判断x1y1指向x2y2的方向
                    // 用反tan求角度 这个高中好像没学过 既然Math类已经帮我们封装好了就直接拿来用吧
                    angle=Math.atan(k) + Math.PI;
                } else {
                    angle=Math.atan(k);
                }
                //这段可写可不写 让计算出来的角度属于-PI/2到PI/2
                if(angle>Math.PI)
                    angle-=Math.PI*2;
                else if(angle<-Math.PI)
                    angle+=Math.PI*2;
            }
            return (float) angle;
        }
    
        public boolean in(float xx, float yy) { //防止小圆被脱太远 拖动范围不超出r1的70%
            double r = Math.sqrt((x1 - xx) * (x1 - xx) + (y1 - yy) * (y1 - yy));//两点间距离公式
            if (r < r1*0.7f)
                return true;
            else return false;
        }
        public boolean isMove(float xx, float yy) { //判断按下摇杆后 是否移动,如果x1y1 x2y2的距离大于r1*0.15视为移动
            // MY实际开发中用到,该教程用不到此变量
            double r = Math.sqrt((x1 - xx) * (x1 - xx) + (y1 - yy) * (y1 - yy));//两点间距离公式
            if (r > r1*0.15f)
                return true;
            else return false;
        }
        public void onDraw(Canvas g, Paint p){ //画摇杆
            if(down) { //当摇杆被按下时 才显示
                //怎么用Canvas画图这里就不说了
                my.re.left = x1 - r1;
                my.re.top = y1 - r1;
                my.re.right = x1 + r1;
                my.re.bottom = y1 + r1;
                g.drawBitmap(img, null, my.re, p); //画大圆
                my.re.left = x2 - r2;
                my.re.top = y2 - r2;
                my.re.right = x2 + r2;
                my.re.bottom = y2 + r2;
                g.drawBitmap(img, null, my.re, p); //画小圆
            }
        }
    }

    新建类 OnTouchMove.java 

    package com.yaogan.liziguo.yaogan;
    
    import android.content.Context;
    import android.graphics.Color;
    import android.view.MotionEvent;
    import android.view.View;
    
    /**
     * Created by Liziguo on 2018/6/16.
     */
    
    public class OnTouchMove extends View { //这个view负责监听移动摇杆的手势
        
        private Move m;
        
        public OnTouchMove(Context context,Move move) {
            super(context);
            this.m=move;
            setBackgroundColor(Color.WHITE);//背景色设为白色
            getBackground().setAlpha(my.ontouchAlpha);//设置触控区透明度
            setOnTouchListener(new OnTouchListener() { //设置触控监听
                @Override
                public boolean onTouch(View v, MotionEvent ev) {
                    //加上getX() getY()因为这个view不是分布在左上角的
                    final float xx = ev.getX() + getX(), yy = ev.getY() + getY();
    
                    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                        m.down(xx, yy);//按下时的操作
    //                    m.move(xx, yy);
                    }
                    m.move(xx, yy);//移动时的操作
                    if (ev.getAction() == MotionEvent.ACTION_UP) {
                        m.up();//松开时的操作
                    }
                    return true;//不要返回false
                }
            });
        }
    }

    修改 Hua.java 的代码

    package com.yaogan.liziguo.yaogan;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.widget.RelativeLayout;
    
    /**
     * Created by Liziguo on 2018/6/15.
     */
    
    public class Hua extends RelativeLayout implements Runnable{ //继承RelativeLayout 实现Runnable接口
    
        private Paint p;//画笔
        private Move m=new Move();//移动摇杆
    
        public Hua(Context context) {
            super(context);
            p=new Paint();
            setBackgroundColor(Color.BLACK);//背景颜色设为黑色
            //实例化一个OnTouchMove
            OnTouchMove onTouchMove=new OnTouchMove(context,m);
            //把onTouchMove添加进来 宽度为屏幕的1/3 高度为屏幕的1/2
            addView(onTouchMove,my.w/3,my.h/2);
            //设置onTouchMove的位置
            onTouchMove.setX(0);
            onTouchMove.setY(my.h/2);
    
            new Thread(this).start();//启动重绘线程
        }
    
        @Override
        protected void onDraw(Canvas g) {//重写onDraw方法
            super.onDraw(g);
            m.onDraw(g,p);//画移动摇杆
        }
    
        @Override
        public void run() { //每隔20毫秒刷新一次画布
            while(true){
                try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
                postInvalidate();//重绘 在子线程重绘不能调用Invalidate()方法
            }
        }
    }

    好的 现在我们的摇杆可以说已经做好一大半了,因为剩下的原理都一样

    先运行一遍看看效果吧

    左下角的白色矩形是我们的OnTouchMove类,为了更好的测试我们先让他显示出来 等做好了再隐藏掉

    下面我们来解释一下为什么斜率k=(x1-x2)/(y1-y2)而不是(y1-y2)/(x1-x2)吧

    因为我们手机上的平面直角坐标系跟数学上的平面直角坐标系不一样

    数学上的平面直角坐标系是这样的

    而我们手机是这样的

    有没有发现把手机的坐标系 逆时针旋转一下就是数学里的坐标系了,不过x跟y调了一下位置

    所以我们在写代码的时候把x y换一下就行了

    数学坐标系中k=1的直线

    程序中k=1的直线

    再解释下 Move 类的 move方法

     

    public void move(float xx,float yy){ //按下摇杆后移动的操作
            angle=getAngle(xx,yy);
            in=in(xx, yy);
            move=isMove(xx,yy);
            if (!in) {
                //下面会做解释
                xx= (float) (x1+ Math.sin(angle)*r1*0.7f);
                yy= (float) (y1+ Math.cos(angle)*r1*0.7f);
            }
            x2=xx;
            y2=yy;
        }

     

    不知不觉已经凌晨2:46了,今天就写到这吧

     

    明天继续。。。。。。

     

    好的下面我们开始做技能摇杆,这教程做的比较累啊

    下面的技能类是我直接从我游戏里拷贝过来的并做了些小修改

    解释可能没那么清楚毕竟原理都一样

    只不过是多了几个功能而已

     

    准备图片

     

    添加到工程里

    由于图片比较多 我们加载图的代码位置改一下

    修改 MainActivity。java 和 my.java

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            my.main=this;
            getSupportActionBar().hide();//隐藏标题栏
            this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏 隐藏状态栏
            //判断当前是否横屏 如果不是就设为横屏,设为横屏之后会自动调用onCreate方法
            if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                //获取屏幕的宽高
                DisplayMetrics dis = getResources().getDisplayMetrics();
                my.w = dis.widthPixels;
                my.h = dis.heightPixels;
                //获取屏幕分辨率和1920*1080的比例 以便适应不同大小的屏幕
                my.bili = (float) (Math.sqrt(my.w * my.h) / Math.sqrt(1920 * 1080));
                //加载图片
                my.border= BitmapFactory.decodeResource(my.main.getResources(),R.mipmap.border);
                my.cancel= BitmapFactory.decodeResource(my.main.getResources(),R.mipmap.cancel);
                my.down= BitmapFactory.decodeResource(my.main.getResources(),R.mipmap.down);
                my.yaogan= BitmapFactory.decodeResource(my.main.getResources(),R.mipmap.yaogan);
                my.cd= BitmapFactory.decodeResource(my.main.getResources(),R.mipmap.cd);
                setContentView(new Hua(this));
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);// 横屏
            }
        }
    }
    public class my { //这个类当一个全局变量使用
        public static int w,h;//屏幕的宽高
        public static float bili;
        public static MainActivity main;
        public static RectF re=new RectF();
        public static int ontouchAlpha=100;//触控区透明度0-255 0为透明,为了测试我们先设为100
    
        public static Bitmap border,cancel,down,yaogan,cd;
    
        public static Skill skill;//当前正在使用的技能 现在会报错 因为我们还没新建技能Skill类
    }

    修改 Move 类的构造方法

        public Move(){
            r1 = 480 * 0.5f * my.bili;//乘上一个比例 适应不同大小的屏幕
            r2 = 300 * 0.5f * my.bili;
    //      img= BitmapFactory.decodeResource(my.main.getResources(),R.mipmap.yaogan);//初始化摇杆图片////////////////////////
            img=my.yaogan;////////////////////////////////////////////////////
        }

     

     

    新建技能类 Skill.java

     

     

    package com.yaogan.liziguo.yaogan;
    
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    
    /**
     * Created by Liziguo on 2018/6/16.
     */
    
    public abstract class Skill {
        public int jineng;
        private final float x,y;//技能图标中心位置,不是按下时的位置
        private float x2, y2;//技能按下移动后手指的坐标
        private float xxx,yyy;//判断拖动点是否超出两倍r的范围
        private final float calcelx, cancely;
        public float angle;//技能按下后 x y指向xx yy的角度
        public Bitmap img, imgborder, imgdown, imgyaogan,imgcd,imgcancel;
        private final float r2;
        private final float r3=50*my.bili;
        public boolean down=false;
        public boolean down_main=false;//down_main 只触发一次;
        public boolean cancel=false;
        public int cdmax;
        public long last,cd=0;//last最后一次释放技能的时间
        /*
        0 普通攻击
        1 技能1
        2 技能2
        3 技能3
         */
        public Skill(int jineng, int cd, Bitmap image){
            this.jineng=jineng;
            switch (jineng){
                case 0:
                    x= my.w*0.87f;
                    y= my.h*0.8f;
                    break;
                case 1:
                    x= my.w*0.7f;
                    y= my.h*0.88f;
                    break;
                case 2:
                    x= my.w*0.75f;
                    y= my.h*0.62f;
                    break;
                case 3:
                    x= my.w*0.9f;
                    y= my.h*0.5f;
                    break;
                default:x=y=0;
            }
            cdmax=cd;
            if(jineng == 0) r2=125*my.bili;
            else r2=80*my.bili;
            calcelx =my.w-r2*2;
            cancely =my.h/4;
            img=image;
            imgborder=my.border;
            imgdown=my.down;
            imgyaogan=my.yaogan;
            imgcd=my.cd;
            imgcancel=my.cancel;
        }
        //    public abstract void down();
    //    public abstract void move();
    //    public abstract void up();
        public void down(){ //DOWN 由ontouch触发
            if(cd>0)return;
            down=true;
            my.skill=this;
        }
        public abstract void down_main(); //DOWN 教程用不到该抽象方法
    
        public void move(float x,float y){//按下技能后 由ontouch触发
            x2 =x;
            y2 =y;
            angle=getAngle(x2, y2);
            cancel=incancel(x,y);
            if (jineng !=0 && !in2(x,y)) {
                xxx= (float) (this.x+ Math.sin(angle)*r2*2);
                yyy= (float) (this.y+ Math.cos(angle)*r2*2);
            }else{
                xxx=x;
                yyy=y;
            }
        }
        public abstract void move_main();//按下技能后 由MyActor触发 教程用不到该抽象方法
        public abstract void up(); //松开后 由MyActor触发 释放技能
    
        public boolean in(float xx,float yy){ //判断是否被点中
            double r= Math.sqrt((x - xx)*(x-xx)+(y-yy)*(y-yy));
            if(r<r2)
                return true;
            else return false;
        }
        public boolean in2(float xx, float yy) { //判断拖动点是否超出两倍r的范围
            double r = Math.sqrt((x - xx) * (x - xx) + (y - yy) * (y - yy));
            if (r < r2 * 2)
                return true;
            else return false;
        }
        public boolean incancel(float xx,float yy){ //判断是否取消
            double r= Math.sqrt((calcelx - xx)*(calcelx -xx)+(cancely -yy)*(cancely -yy));
            if(r<r2)
                return true;
            else return false;
        }
        public float getAngle(float xx,float yy){ //x y指向xx yy的角度
            float angle,k;
            if (y==yy)
                if (x > xx)
                    angle= (float) (-Math.PI/2);
                else
                    angle= (float) (Math.PI/2);
            else{
                k=(x-xx)/(y-yy);
                if (y > yy) {
                    angle= (float) (Math.atan(k) + Math.PI);
                } else {
                    angle= (float) Math.atan(k);
                }
                if(angle>Math.PI)
                    angle-=Math.PI*2;
                else if(angle<-Math.PI)
                    angle+=Math.PI*2;
    
            }
            return angle;
        }
        private float drawpx=10*my.bili;
    
        public void next(){
            //计算技能冷却时间
            cd=cdmax-System.currentTimeMillis()+last;
        }
        //按下的时候技能图标下移 显示蓝色框框
        public void onDraw(Canvas g, Paint p){
            my.re.left=x-r2;
            my.re.top=y-r2;
            my.re.right=x+r2;
            my.re.bottom=y+r2;
            if(down){
    //            new RectF(x-r2,y-r2,x+r2,y+r2);
    //            new RectF(x-r2,y-r2+10*my.bili,x+r2,y+r2+10*my.bili);
    //            my.re.left=x-r2;
    //            my.re.top=y-r2;
    //            my.re.right=x+r2;
    //            my.re.bottom=y+r2;
                if(jineng!=0){
                    final float bl=2;
                    my.re.left=x-r2*bl;
                    my.re.top=y-r2*bl;
                    my.re.right=x+r2*bl;
                    my.re.bottom=y+r2*bl;
                    //蓝色框框未下移
                    g.drawBitmap(imgdown,null,my.re,p);
                }
                my.re.left=x-r2;
                my.re.top=y-r2;
                my.re.right=x+r2;
                my.re.bottom=y+r2;
                ///////////////////////////////////////////////////////////
                //技能图片和技能边框下移
                my.re.top+=drawpx;
                my.re.bottom+=drawpx;
                g.drawBitmap(img,null,my.re,p);
                my.re.left-=drawpx;
                my.re.top-=drawpx;
                my.re.right+=drawpx;
                my.re.bottom+=drawpx;
    
                g.drawBitmap(imgborder,null,my.re,p);
                if(jineng!=0){
                    my.re.left=xxx-r3;
                    my.re.top=yyy-r3;
                    my.re.right=xxx+r3;
                    my.re.bottom=yyy+r3;
                    g.drawBitmap(imgyaogan,null,my.re,p);
                    //cancle
                    my.re.left= calcelx -r2;
                    my.re.top= cancely -r2;
                    my.re.right= calcelx +r2;
                    my.re.bottom= cancely +r2;
                    g.drawBitmap(imgcancel,null,my.re,p);
                }
            }else{
                g.drawBitmap(img,null,my.re,p);
                if(jineng!=0 && cd>0) {
                    p.setTextSize(40*my.bili);
                    p.setColor(Color.WHITE);
                    g.drawBitmap(imgcd,null,my.re,p);
                    float f=cd/100f;
                    f=(int)f;
                    f=f/10;
                    g.drawText(String.valueOf(f),x-p.getTextSize()*4/5,y+p.getTextSize()/3,p);
    
                }
                my.re.left-=drawpx;
                my.re.top-=drawpx;
                my.re.right+=drawpx;
                my.re.bottom+=drawpx;
                g.drawBitmap(imgborder,null,my.re,p);
            }
        }
    }

    新建一个类 OnTouchSkill.java 他也是一个监听view

    这样多点触控会好写很多,刚开始我是用一个view做监听的 写到我心态爆炸。。。

    package com.yaogan.liziguo.yaogan;
    
    import android.content.Context;
    import android.graphics.Color;
    import android.view.MotionEvent;
    import android.view.View;
    
    /**
     * Created by Liziguo on 2018/6/16.
     */
    
    public class OnTouchSkill extends View {
        /*
        A 普通攻击
        Q 技能1
        W 技能2
        E 技能3
        R 没有R
         */
    
        public Skill A,Q,W,E;
    
        public OnTouchSkill(Context context,Skill a,Skill q,Skill w,Skill e) {
            super(context);
            A=a;Q=q;W=w;E=e;
            setBackgroundColor(Color.WHITE);
            getBackground().setAlpha(my.ontouchAlpha);//0-255
            setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent ev) {
                    final float xx = ev.getX() + getX(), yy = ev.getY() + getY();
                    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                        if ( A.in(xx, yy)) {
                            A.down();
                        } else if ( Q.in(xx, yy)) {
                            Q.down();
                        } else if ( W.in(xx, yy)) {
                            W.down();
                        } else if ( E.in(xx, yy)) {
                            E.down();
                        }
    
                    }
                    if (my.skill != null) my.skill.move(xx, yy);
                    if(ev.getAction()==MotionEvent.ACTION_UP){
                        A.down = false;
                        Q.down = false;
                        W.down = false;
                        E.down = false;
                    }
                    return true;
                }
            });
        }
    }

    把监听控件添加到Hua,修改 Hua.java

    package com.yaogan.liziguo.yaogan;
    
    import android.content.Context;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.widget.RelativeLayout;
    
    /**
     * Created by Liziguo on 2018/6/15.
     */
    
    public class Hua extends RelativeLayout implements Runnable{ //继承RelativeLayout 实现Runnable接口
    
        private Paint p;//画笔
        private Move m=new Move();//移动摇杆
    
         /*
        A 普通攻击
        Q 技能1
        W 技能2
        E 技能3
        R 没有R
         */
    
        public Skill A=new Skill(0,100, BitmapFactory.decodeResource(getResources(),R.mipmap.putonggongji)) {
            @Override
            public void down_main() { }
            @Override
            public void move_main() { }
            @Override
            public void up() { }
        };
        public Skill Q=new Skill(1,1000, BitmapFactory.decodeResource(getResources(),R.mipmap.skill1)) {
            @Override
            public void down_main() { }
            @Override
            public void move_main() { }
            @Override
            public void up() {
                down_main=false;
                if(!cancel){ //技能冷却时间
                    last= System.currentTimeMillis();
                }
            }
        };
        public Skill W=new Skill(2,1000, BitmapFactory.decodeResource(getResources(),R.mipmap.skill2)) {
            @Override
            public void down_main() { }
            @Override
            public void move_main() { }
            @Override
            public void up() {
                down_main=false;
                if(!cancel){
                    last= System.currentTimeMillis();
                }
            }
        };
        public Skill E=new Skill(3,1000, BitmapFactory.decodeResource(getResources(),R.mipmap.skill3)) {
            @Override
            public void down_main() { }
            @Override
            public void move_main() { }
            @Override
            public void up() {
                down_main=false;
                if(!cancel){
                    last= System.currentTimeMillis();
                }
            }
        };
    
        public Hua(Context context) {
            super(context);
            p=new Paint();
            setBackgroundColor(Color.BLACK);//背景颜色设为黑色
            //实例化一个OnTouchMove
            OnTouchMove onTouchMove=new OnTouchMove(context,m);
            //把onTouchMove添加进来 宽度为屏幕的1/3 高度为屏幕的1/2
            addView(onTouchMove,my.w/3,my.h/2);
            //设置onTouchMove的位置
            onTouchMove.setX(0);
            onTouchMove.setY(my.h/2);
    
            //添加技能摇杆监听
            OnTouchSkill onTouchSkill=new OnTouchSkill(context,A,Q,W,E);//后添加的优先级高
            addView(onTouchSkill);
            onTouchSkill.setX(my.w*0.7f-85*my.bili);
            onTouchSkill.setY(my.h/2-85*my.bili);
            new Thread(this).start();//启动重绘线程
        }
    
        @Override
        protected void onDraw(Canvas g) {//重写onDraw方法
            super.onDraw(g);
            m.onDraw(g,p);//画移动摇杆
            //画技能
            A.onDraw(g,p);
            Q.onDraw(g,p);
            W.onDraw(g,p);
            E.onDraw(g,p);
        }
    
        @Override
        public void run() { //每隔20毫秒刷新一次画布
            while(true){
                try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
                //计算冷却时间
                A.next();
                Q.next();
                W.next();
                E.next();
                //释放技能
                if (my.skill != null) {
                    my.skill.down_main();//教程用不到该方法
                    my.skill.move_main();//教程用不到该方法
                    if (my.skill.down == false) {
                        my.skill.up();
                        my.skill = null;
                    }
                }
    
                postInvalidate();//重绘 在子线程重绘不能调用Invalidate()方法
            }
        }
    }

    运行下看看效果吧

    修改 my.java

    public class my { //这个类当一个全局变量使用
        public static int w,h;//屏幕的宽高
        public static float bili;
        public static MainActivity main;
        public static RectF re=new RectF();
        public static int ontouchAlpha=0;//把触控区透明度改成0
    
        public static Bitmap border,cancel,down,yaogan,cd;
    
        public static Skill skill;//当前正在使用的技能
    }

     

    再运行下

     

    大功告成

    下载地址:https://download.csdn.net/download/u010756046/10482434

    PS:图片不要太大 尽量压缩到20kb以下,不然使用摇杆会很卡

     

    展开全文
  • 基于地理位置服务的Android平台的开发对Android移动开发来说是非常重要的,基于地理位置服务的Android平台的开发是主要用于Android系统作为载体,我们可以利用定位出的位置进行许多丰富多彩的操作。比如说天气预报...

    基于地理位置服务的Android平台的开发对Android移动开发来说是非常重要的,基于地理位置服务的Android平台的开发是主要用于Android系统作为载体,我们可以利用定位出的位置进行许多丰富多彩的操作。比如说天气预报程序可以根据用户所在的位置自动选择城市,发微博的时候我们可以向朋友晒一下自己的地理位置,不认识路的时候随时打开地图就可以查询路线;如果你出门打车用滴滴或Uber打车,你可以看到附近司机所在的位置,当你创建订单时司机可以获取你所在的位置;如果想获取自己所在位置的周边的服务,我们可以打开高德地图或美团就能获取到的周边的小吃、银行、医院和电影院等。
    那么如何在Android项目里调用基于百度地图API实现定位的开发呢?

    1. 申请百度地图API Key

    要想在自己的应用程序里加入百度地图的功能,首先必须申请一个API Key。你得拥有一个百度账号才能申请,申请好百度账号后那么就得在百度地图开放平台注册成为一名百度开发者。登录百度账号,并打开http://lbsyun.baidu.com/这个网址,在这里填写一些注册信息即可,如下图所示:

    这里写图片描述

    这里写图片描述

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ckl48RTz-1573318889378)(https://img-blog.csdn.net/20171207201949633?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnVrYWltZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CVQ3tegh-1573318889378)(https://img-blog.csdn.net/20171207180356927?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnVrYWltZWk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)]

    由于这是一个刚刚注册的账号,所以目前的应用列表是空的。接下来点击创建应用就可以去申请API Key了,应用名称可以随便填,应用类型选择Android SDK,启用服务保持默认即可(最好全选),包名就是填写你要申请API Key应用到哪个应用上的包名,包名必须一致,否则应用上的地图的画面加载不出来。如下图所示:

    这里写图片描述

    那么剩下的发布版SHA1和开发版SHA1是什么意思呢?这是我们申请API Key所必须填写的字段,发布版SHA1就是你自己对你所开发的App进行数字加密签名的指纹证书,防止别人对的App进行反编译的操作,也是为了证明该App是你个人开发的;开发版SHA1是指在Android Studio或Eclipse上创建一个项目是默认生成的一个数字指纹证书,注意它们都是唯一的,如果Android Studio或Eclipse再重新安装Android SDK时会又随机生成不同的开发版SHA1数字指纹证书。

    那么如何在Android Studio上获取开发版SHA1值和发布版SHA1值?

    • 开发版的SHA1值的获取方法如下:

    这里写图片描述

    • 发布版的SHA1值的获取方法如下:

    要想获取发布版的SHA1的值,必须先对项目进行签名,具体是如何对项目进行签名的,详情请看我之前发表的博客:Android移动开发-如何在AndroidStudio里进行对应用程序进行签名,项目签名后可以直接用Android Studio提供的命令控制台Terminal输入命令时输入“cd 自己应用签名的jks文件保存的文件目录”然后接着回车,比如:cd D:\AndroidStudio\AndroidKey\BaiduMapTest1,接着在命令行里输入keytool -list -v -keystore jks文件名,比如:keytool -list -v -keystore BaiduMapTest.jks,接着按回车,然后输入秘钥,回车(秘钥库口令是看不到的)。如下图所示:

    这里写图片描述

    然后在百度地图开发者中心的创建应用里分别输入开发版和发布版的SHA1的值的后点击提交,就能申请到了百度地图的API Key了。如下图所示:

    这里写图片描述

    • 让地图显示到自己的应用中

    现在正是趁热打铁的好时机,新建一个百度地图测试的项目,项目名称为BaiduMapTest,包名为com.fukaimei.BaiduMapTest(注意:必须在百度地图里申请API Key时填写的包名一致),在开始编码之前,我们还需要先将百度地图Android版的SDK准备好,下载地址为:http://lbsyun.baidu.com/index.php?title=sdk/download&action#selected=location_all,然后点击一键下载按钮即可。
    下载完成后对压缩包解压,并在Android Studio里做相应的环境配置。
    第一步:在工程app/libs目录下放入baidumapapi_vX_X_X.jar包,在src/main/目录下新建jniLibs目录,工程会自动加载src目录下的so动态库,放入libBaiduMapSDK_vX_X_X_X.so如下图所示,注意jar和so的前3位版本号必须一致,并且保证使用一次下载的文件夹中的两个文件,不能不同功能组件的jar或so交叉使用。

    这里写图片描述

    so的配置也可以参考demo给出的目录结构,如下图所示,在app工程路径下,新建libs,并在libs目录下放入对应不同CPU架构的so文件。这样工程并不会自动加载libs下的so,需在gradle编译时,通过加入代码: jniLibs.srcDir ‘libs’ 来说明so的路径为该libs路径。

    这里写图片描述

    这里写图片描述

    第二步:工程配置还需要把jar包集成到自己的工程中,如图上图所示,放入libs目录下。对于每个jar文件,右键-选择Add As Library,导入到工程中。对应在build.gradle生成工程所依赖的jar文件说明,如下图所示:

    这里写图片描述

    集成地图SDK的应用,在打包混淆的时候,需要注意与地图SDK相关的方法不可被混淆。混淆方法如下:

    -keep class com.baidu.** {*;}
    -keep class vi.com.** {*;}    
    -dontwarn com.baidu.**
    

    保证百度类不能被混淆,否则会出现网络不可用等运行时异常。
    百度地图SDK为开发者提供了便捷的显示百度地图数据的接口,通过以下几步操作,即可在您的应用中使用百度地图数据:
    第一步:创建并配置工程;
    第二步:在AndroidManifest中添加开发密钥、所需权限等信息;

    • (1)在AndroidManifest.xml清单文件中添加开发密钥,如下图所示:

    这里写图片描述

    • (2)在AndroidManifest.xml清单文件中添加所需权限,如下图所示:
            <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
        <uses-permission android:name="android.permission.WAKE_LOCK" />
        <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
        <uses-permission android:name="android.permission.GET_TASKS" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.WRITE_SETTINGS" />
        <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    
        <uses-permission android:name="android.permission.CAMERA" />
        <uses-permission android:name="android.permission.VIBRATE" />
        <uses-permission android:name="android.permission.FLASHLIGHT" />
    
    • 第三步,在布局xml文件中添加地图控件;
    <com.baidu.mapapi.map.MapView  
        android:id="@+id/bmapView"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"  
        android:clickable="true" />
    
    • 第四步,在应用程序创建时初始化 SDK引用的Context 全局变量:
    public class MainActivity extends Activity {  
        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);   
            //在使用SDK各组件之前初始化context信息,传入ApplicationContext  
            //注意该方法要再setContentView方法之前实现  
            SDKInitializer.initialize(getApplicationContext());  
            setContentView(R.layout.activity_main);  
        }  
    }
    

    注意:在SDK各功能组件使用之前都需要调用。
    SDKInitializer.initialize(getApplicationContext());,因此我们建议该方法放在Application的初始化方法中。

    • 第五步,创建地图Activity,管理地图生命周期;
    public class MainActivity extends Activity {  
        MapView mMapView = null;  
        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);   
            //在使用SDK各组件之前初始化context信息,传入ApplicationContext  
            //注意该方法要再setContentView方法之前实现  
            SDKInitializer.initialize(getApplicationContext());  
            setContentView(R.layout.activity_main);  
            //获取地图控件引用  
            mMapView = (MapView) findViewById(R.id.bmapView);  
        }  
        @Override  
        protected void onDestroy() {  
            super.onDestroy();  
            //在activity执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理  
            mMapView.onDestroy();  
        }  
        @Override  
        protected void onResume() {  
            super.onResume();  
            //在activity执行onResume时执行mMapView. onResume (),实现地图生命周期管理  
            mMapView.onResume();  
            }  
        @Override  
        protected void onPause() {  
            super.onPause();  
            //在activity执行onPause时执行mMapView. onPause (),实现地图生命周期管理  
            mMapView.onPause();  
            }  
        }
    
    • 在应用添加地图定位的功能

    (1)在AndroidManifest.xml清单文件中声明SERVICE组件,每个APP拥有自己单独的定位SERVICE,如下图所示:

            <service
                android:name="com.baidu.location.f"
                android:enabled="true"
                android:process=":remote">
            </service>
    
    • (2)在AndroidManifest.xml清单文件中添加所需权限,如下图所示:
     <!-- 这个权限用于进行网络定位-->
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
        <!-- 这个权限用于访问GPS定位-->
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
        <!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位-->
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
        <!-- 获取运营商信息,用于支持提供运营商信息相关的接口-->
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
        <!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
        <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
        <!-- 用于读取手机当前的状态-->
        <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
        <!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据-->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
        <!-- 访问网络,网络定位需要上网-->
        <uses-permission android:name="android.permission.INTERNET" />
        <!-- SD卡读取权限,用户写入离线定位数据-->
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
    
    • 在应用添加地图的其它功能(卫星地图、实时交通、跟随模式和罗盘模式等)
    package com.fukaimei.baidumaptest;
    
    import android.Manifest;
    import android.app.Activity;
    import android.content.Context;
    import android.content.pm.PackageManager;
    import android.os.Build;
    import android.os.Bundle;
    import android.support.v4.app.ActivityCompat;
    import android.support.v7.widget.PopupMenu;
    import android.view.MenuItem;
    import android.view.View;
    import android.widget.Button;
    import android.widget.Toast;
    
    import com.baidu.location.BDLocation;
    import com.baidu.location.BDLocationListener;
    import com.baidu.location.LocationClient;
    import com.baidu.location.LocationClientOption;
    import com.baidu.mapapi.SDKInitializer;
    import com.baidu.mapapi.map.BaiduMap;
    import com.baidu.mapapi.map.BitmapDescriptor;
    import com.baidu.mapapi.map.BitmapDescriptorFactory;
    import com.baidu.mapapi.map.MapStatus;
    import com.baidu.mapapi.map.MapStatusUpdate;
    import com.baidu.mapapi.map.MapStatusUpdateFactory;
    import com.baidu.mapapi.map.MapView;
    import com.baidu.mapapi.map.MyLocationConfiguration;
    import com.baidu.mapapi.map.MyLocationData;
    import com.baidu.mapapi.model.LatLng;
    
    public class MainActivity extends Activity {
    
        private static final int BAIDU_READ_PHONE_STATE = 100;
    
        private MapView mMapView = null;
        private BaiduMap mBaiduMap;
        private LocationClient mlocationClient;
        private MylocationListener mlistener;
        private Context context;
    
        private double mLatitude;
        private double mLongitude;
        private float mCurrentX;
    
        private Button mGetMylocationBN;
    
        PopupMenu popup = null;
    
        //自定义图标
        private BitmapDescriptor mIconLocation;
    
        private MyOrientationListener myOrientationListener;
        //定位图层显示方式
        private MyLocationConfiguration.LocationMode locationMode;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            SDKInitializer.initialize(getApplicationContext());
            setContentView(R.layout.activity_main);
            this.context = this;
    
            initView();
            //判断是否为Android 6.0 以上的系统版本,如果是,需要动态添加权限
            if (Build.VERSION.SDK_INT >= 23) {
                showLocMap();
            } else {
                initLocation();//initLocation为定位方法
            }
    
        }
    
        private void initView() {
            mMapView = (MapView) findViewById(R.id.id_bmapView);
    
            mBaiduMap = mMapView.getMap();
            //根据给定增量缩放地图级别
            MapStatusUpdate msu = MapStatusUpdateFactory.zoomTo(18.0f);
            mBaiduMap.setMapStatus(msu);
            MapStatus mMapStatus;//地图当前状态
            MapStatusUpdate mMapStatusUpdate;//地图将要变化成的状态
            mMapStatus = new MapStatus.Builder().overlook(-45).build();
            mMapStatusUpdate = MapStatusUpdateFactory.newMapStatus(mMapStatus);
            mBaiduMap.setMapStatus(mMapStatusUpdate);
            mGetMylocationBN = (Button) findViewById(R.id.id_bn_getMyLocation);
            mGetMylocationBN.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    getMyLocation();
                }
            });
        }
    
        /**
         * 定位方法
         */
        private void initLocation() {
            locationMode = MyLocationConfiguration.LocationMode.NORMAL;
    
            //定位服务的客户端。宿主程序在客户端声明此类,并调用,目前只支持在主线程中启动
            mlocationClient = new LocationClient(this);
            mlistener = new MylocationListener();
    
            //注册监听器
            mlocationClient.registerLocationListener(mlistener);
            //配置定位SDK各配置参数,比如定位模式、定位时间间隔、坐标系类型等
            LocationClientOption mOption = new LocationClientOption();
            //设置坐标类型
            mOption.setCoorType("bd09ll");
            //设置是否需要地址信息,默认为无地址
            mOption.setIsNeedAddress(true);
            //设置是否打开gps进行定位
            mOption.setOpenGps(true);
            //设置扫描间隔,单位是毫秒,当<1000(1s)时,定时定位无效
            int span = 1000;
            mOption.setScanSpan(span);
            //设置 LocationClientOption
            mlocationClient.setLocOption(mOption);
    
            //初始化图标,BitmapDescriptorFactory是bitmap 描述信息工厂类.
            mIconLocation = BitmapDescriptorFactory
                    .fromResource(R.drawable.icon_geo);
    
            myOrientationListener = new MyOrientationListener(context);
            //通过接口回调来实现实时方向的改变
            myOrientationListener.setOnOrientationListener(new MyOrientationListener.OnOrientationListener() {
                @Override
                public void onOrientationChanged(float x) {
                    mCurrentX = x;
                }
            });
    
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            //开启定位
            mBaiduMap.setMyLocationEnabled(true);
            if (!mlocationClient.isStarted()) {
                mlocationClient.start();
            }
            myOrientationListener.start();
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            //停止定位
            mBaiduMap.setMyLocationEnabled(false);
            mlocationClient.stop();
            myOrientationListener.stop();
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            mMapView.onResume();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            mMapView.onPause();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mMapView.onDestroy();
        }
    
        public void getMyLocation() {
            LatLng latLng = new LatLng(mLatitude, mLongitude);
            MapStatusUpdate msu = MapStatusUpdateFactory.newLatLng(latLng);
            mBaiduMap.setMapStatus(msu);
        }
    
        public void onPopupMenuClick(View v) {
            // 创建PopupMenu对象
            popup = new PopupMenu(this, v);
            // 将R.menu.menu_main菜单资源加载到popup菜单中
            getMenuInflater().inflate(R.menu.menu_main, popup.getMenu());
            // 为popup菜单的菜单项单击事件绑定事件监听器
            popup.setOnMenuItemClickListener(
                    new PopupMenu.OnMenuItemClickListener() {
                        @Override
                        public boolean onMenuItemClick(MenuItem item) {
                            switch (item.getItemId()) {
                                case R.id.id_map_common:
                                    mBaiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL);
                                    break;
                                case R.id.id_map_site:
                                    mBaiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE);
                                    break;
                                case R.id.id_map_traffic:
                                    if (mBaiduMap.isTrafficEnabled()) {
                                        mBaiduMap.setTrafficEnabled(false);
                                        item.setTitle("实时交通(off)");
                                    } else {
                                        mBaiduMap.setTrafficEnabled(true);
                                        item.setTitle("实时交通(on)");
                                    }
                                    break;
                                case R.id.id_map_mlocation:
                                    getMyLocation();
                                    break;
                                case R.id.id_map_model_common:
                                    //普通模式
                                    locationMode = MyLocationConfiguration.LocationMode.NORMAL;
                                    break;
                                case R.id.id_map_model_following:
                                    //跟随模式
                                    locationMode = MyLocationConfiguration.LocationMode.FOLLOWING;
                                    break;
                                case R.id.id_map_model_compass:
                                    //罗盘模式
                                    locationMode = MyLocationConfiguration.LocationMode.COMPASS;
                                    break;
                            }
                            return true;
                        }
                    });
            popup.show();
        }
    
        /**
         * 所有的定位信息都通过接口回调来实现
         */
        public class MylocationListener implements BDLocationListener {
            //定位请求回调接口
            private boolean isFirstIn = true;
    
            //定位请求回调函数,这里面会得到定位信息
            @Override
            public void onReceiveLocation(BDLocation bdLocation) {
                //BDLocation 回调的百度坐标类,内部封装了如经纬度、半径等属性信息
                //MyLocationData 定位数据,定位数据建造器
                /**
                 * 可以通过BDLocation配置如下参数
                 * 1.accuracy 定位精度
                 * 2.latitude 百度纬度坐标
                 * 3.longitude 百度经度坐标
                 * 4.satellitesNum GPS定位时卫星数目 getSatelliteNumber() gps定位结果时,获取gps锁定用的卫星数
                 * 5.speed GPS定位时速度 getSpeed()获取速度,仅gps定位结果时有速度信息,单位公里/小时,默认值0.0f
                 * 6.direction GPS定位时方向角度
                 * */
                mLatitude = bdLocation.getLatitude();
                mLongitude = bdLocation.getLongitude();
                MyLocationData data = new MyLocationData.Builder()
                        .direction(mCurrentX)//设定图标方向
                        .accuracy(bdLocation.getRadius())//getRadius 获取定位精度,默认值0.0f
                        .latitude(mLatitude)//百度纬度坐标
                        .longitude(mLongitude)//百度经度坐标
                        .build();
                //设置定位数据, 只有先允许定位图层后设置数据才会生效,参见 setMyLocationEnabled(boolean)
                mBaiduMap.setMyLocationData(data);
                //配置定位图层显示方式,三个参数的构造器
                /**
                 * 1.定位图层显示模式
                 * 2.是否允许显示方向信息
                 * 3.用户自定义定位图标
                 * */
                MyLocationConfiguration configuration
                        = new MyLocationConfiguration(locationMode, true, mIconLocation);
                //设置定位图层配置信息,只有先允许定位图层后设置定位图层配置信息才会生效,参见 setMyLocationEnabled(boolean)
                mBaiduMap.setMyLocationConfigeration(configuration);
                //判断是否为第一次定位,是的话需要定位到用户当前位置
                if (isFirstIn) {
                    //地理坐标基本数据结构
                    LatLng latLng = new LatLng(bdLocation.getLatitude(), bdLocation.getLongitude());
                    //描述地图状态将要发生的变化,通过当前经纬度来使地图显示到该位置
                    MapStatusUpdate msu = MapStatusUpdateFactory.newLatLng(latLng);
                    //改变地图状态
                    mBaiduMap.setMapStatus(msu);
                    isFirstIn = false;
                    Toast.makeText(context, "您当前的位置为:" + bdLocation.getAddrStr(),Toast.LENGTH_LONG).show();
                }
            }
        }
    
        /**
         * Android 6.0 以上的版本的定位方法
         */
        public void showLocMap() {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
                    != PackageManager.PERMISSION_GRANTED
                    || ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                    != PackageManager.PERMISSION_GRANTED
                    || ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
                    != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(getApplicationContext(), "没有权限,请手动开启定位权限", Toast.LENGTH_SHORT).show();
                // 申请一个(或多个)权限,并提供用于回调返回的获取码(用户定义)
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{
                        Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE
                }, BAIDU_READ_PHONE_STATE);
            } else {
                initLocation();
            }
        }
    
        //Android 6.0 以上的版本申请权限的回调方法
        @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            switch (requestCode) {
                // requestCode即所声明的权限获取码,在checkSelfPermission时传入
                case BAIDU_READ_PHONE_STATE:
                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        // 获取到权限,作相应处理(调用定位SDK应当确保相关权限均被授权,否则可能引起定位失败)
                        initLocation();
                    } else {
                        // 没有获取到权限,做特殊处理
                        Toast.makeText(getApplicationContext(), "获取位置权限失败,请手动开启", Toast.LENGTH_SHORT).show();
                    }
                    break;
                default:
                    break;
            }
        }
    }
    
    • 完成以上步骤后,运行程序,即可在您的应用中显示相应的功能,如下地图:

    这里写图片描述 这里写图片描述
    ———————— The end ————————

    如果您觉得这篇博客写的比较好的话,赞赏一杯咖啡吧~~
    在这里插入图片描述


    Demo程序源码下载地址一(GitHub)
    Demo程序源码下载地址二(Gitee)

    展开全文
  • Android移动应用开发》 复习题(一) (一)Android的体系结构是怎样的?请简要加以说明。 Android的系统架构采用了分层架构的思想,从上层到底层共包括四层,分别是应用层、应用框架层、系统运行库层以及Linux...

    《Android移动应用开发》

    复习题(一)

    (一)Android的体系结构是怎样的?请简要加以说明。

    Android的系统架构采用了分层架构的思想,从上层到底层共包括四层,分别是应用层、应用框架层、系统运行库层以及Linux内核层。
    应用层:Android平台不仅仅是操作系统,也包含了许多应用程序,譬如SMS短信客户端程序、电话拨号程序、图片浏览器、Web浏览器等应用。这些应用程序都是用Java语言编写的,并且可以被开发人员开发的其他应用程序所替换。
    应用框架层:该层是Android应用开发的基础。应用框架层包括活动管理器、窗口管理器、内容提供者、视图系统、包管理器、电话管理器、资源管理器、位置管理器、通知管理器和XMPP服务十个部分。
    系统运行库层:系统运行库层可以分成两部分,分别是系统库和Android运行时。
    Linux内核层:Android的核心系统服务依赖于Linux内核,如安全性、内存管理、进程管理、网络协议栈和驱动模型。Linux内核也同时作为硬件和软件栈之间的抽象层。

    (二)Android程序结构是怎样的?请简要加以分析。

    解答:如以工程名称Firstdemo作为根目录,将所有自动生成的和非自动生成的文件都保存在这个根目录下。
    (1)src目录:src目录下是java源代码存放目录,里面一般都是.java结尾的java文件。
    (2)gen目录:gen目录是Android开发工具自动生成的文件。目录中有个可自定义的包,包里有两个文件,BuildConfig.java和R.java。BuildConfig.java是Android调试用的。R.java是在建立项目时自动生成的,这个文件是只读模式,不能更改。
    (3)res目录:res目录是资源目录,可以存放应用使用到的各种资源,如XML界面文件、图片、数据等。
    (4)assets目录:assets资源目录一般可用于存放html文件、数据库文件、javascript文件等,还有原始格式的文件,例如二进制格式的音频文件、视频文件等。
    (5)AndroidMamifest.xml
    AndroidMamifest.xml项目清单文件列出了应用程序提供的功能,开发好的各种组件需要在此文件中进行配置,当应用使用到系统内置的应用(如电话服务、互联网服务、短信服务、GPS服务等)还需在此文件中声明使用权限。每个Android程序必须在根目录下包含一个AndroidMamifest.xml文件。

    (三)Activity的四种启动模式是什么?并请加以说明。
    Standard、single Top、single Task、single Instance
    Standard:默认模式,会在启动时创建一个新实例,创建的模式也可以随Intent.FLAG_ACTIVITY_NEW_TASK而改变
    single Top:当启动activity时,有相同的activity在前台与用户交互,那就复用这个activity,这个实例会被调用Activity.onNewIntent()。
    single Task:在启动activity时,若有一个运行着这个activity的task,那这个activity实例会被调到前台,并调用Activity.onNewIntent() ,启动实例的Intent的flag会被设置Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT . singleTask是singleTop的一个扩展集。
    single Instance:开辟一个只允许一个activity实例在里头运行的task. 如果用同样的intent再次启动这个activity,task会被调到前台,其Activity.onNewIntent() 会被调用. 如果这个activity实例要启动一个新activity,那么这个新activity会在一个新task中运行.

    (四)Handler消息传递机制是怎样的?举例加以说明。

    利用Handler消息传递的过程是:使用Handler发送消息,该消息被传送到指定的Message Queue。为了保证正常工作,当前线程必须有Message Queue,而Message Queue 是由Looper 对象来管理的。因此要求当前线程必须有一个Looper对象,根据不同类型的线程,处理情况不同,主要分为以下两类:
    主线程:系统以及为其初始化了Looper对象,因此可以直接创建Handler对象,并由该Handler对象发送,处理消息。
    非主线程:先调用Looper的prepare()方法创建一个Looper对象,然后调用Looper的loop()方法启动它。
    当线程有了Looper对象之后,再创建Handler子类的实例,重写handleMessage()方法处理消息,最后由loop()启动Looper对象。

    (五)如何发送广播?如何接收系统广播消息?
    Android广播分为两个方面:广播发送者和广播接收者。通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器),用于异步接收广播Intent。广播Intent是通过调用Context.sendBroadcast()发送、BroadcastReceiver()接收。广播Intent的发送是通过调用Context.sendBroadcast()、Context.sendOrderedBroadcast()、Context.sendStickyBroadcast()来实现的。通常一个广播Intent可以被订阅了此Intent的多个广播接收者所接收。
    广播接收器只能接收广播,对广播的通知做出反应,很多广播都产生于系统代码,如:时区改变的通知、电池电量不足、用户改变了语言偏好,或者开机启动等。
    广播接收器没有用户界面,但是它可以为它们接收到信息启动一个Activity或者使用NotificationManager来通知用户。

    解答:Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开机启动,网络状态改变,拍照,屏幕关闭与开启,点亮不足等等。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,由系统自动发出。(编程实现略。)

    (六)Service 和 Thread 的区别是什么?为什么使用Service?注意事项是什么?

    Thread是程序执行的最小单元,可以用Thread来执行一些异步操作。而Service是Android的一种机制,当它运行的时候如果是Local Service,那么对应的Service是运行在主进程的main线程中的。如果是Remote Service,那么对应的Service则是运行在独立进程的main线程中。
    Service可以用来处理一些比较复杂的操作,并且不会因为系统内存紧张而被“杀掉”。
    Service不是一个单独的进程,除非单独声明,否则它不会运行在单独的进程中,而是和启动它的程序运行在同一进程中。Service也不是线程,这意味着它将在主线程里运行。

    (七)比较进程内服务与跨进程服务的不同。

    进程内服务:同一个进程下调用的服务, (通常情况下)即在一个应用程序下的服务。
    跨进程服务:通过一个应用程序(客户端)的 Activity 调用另一个应用程序(服务端)的 Service 为跨进程服务。在 Android 中,如果需要在不同进程间实现通信,就需要用到 AIDL 技术去完成。

    (八)Executor、ExecutorService和Executors的区别是什么?

    Executor 是一个简单的标准化接口,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。根据所使用的具体 Executor 类的不同,可能在新创建的线程中,现有的任务执行线程中,或者调用 execute() 的线程中执行任务,并且可能顺序或并发执行。
    ExecutorService 提供了多个完整的异步任务执行框架。 ExecutorService 管理任务的排队和安排, 并允许受控制的关闭。
    Executors 类提供大多数 Executor 的常见类型和配置的工厂方法, 以及使用它们的几种实用工具方法
    Java 里面线程池的顶级接口是 Executor ,但是严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是 ExecutorService 。ExecutorService 继承Executor 。Executors 类为创建 ExecutorService 提供了便捷的工厂方法。

    (九)ThreadPoolExecutor和ExecutorService有怎样的关系?ThreadPoolExecutor的构造方法是怎样的?试对ThreadPoolExecutor构造方法的参数加以说明。

    关系:ThreadPoolExecutor是ExecutorSevice的一个实现类, 它使用可能的儿个池线程之一执行每个提交的任务,通常使用Executors工厂方法配置。
    方法:1. 用给定的初始参数和默认的线程工厂及被拒绝的执行处理程序创建新的ThreadPoolExecutor.
    2. 用给定的初始参数和默认的线程工厂创建新的ThreadpoolExecutor.
    3. 用给定的初始参数和默认被拒绝的执行处理程序创建新的ThreadPoolExecutor.
    4. 用给定的初始参数创建新的ThreadPoolExecutor.
    说明:✧corePoolSize: 池中所保存的线程数,包括空闲线程。
    ✧maximumPoolSize: 池中允许的最大线程数。
    ✧keepAliveTime: 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
    ✧unit: keepAliveTime 参数的时间单位。
    ✧workQueue;:执行前用于保持任务的队列。此队列仅保持由execute 方法提交的
    Runnable任务。
    ✧threadFactory;:执行程序创建新线程时使用的工厂,
    ✧Handler: 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。

    (十)为什么说Executors类为创建ExecutorService提供了便捷的工厂方法?

    要配置一个线程池是比较复杂的, 尤其是对于线程池的原理不是很清楚的情况下, 很有可能配置的线程池不是较优的, 因此在 Executors 类里面提供了一些静态工厂, 生成一些常用的线程池。
    ( 1) newCachedThreadPool : 创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲( 60 秒不执行任务)的线程,当任务数增加时,此线程池又可以智能地添加新线程来处理任务。 此线程池不会对线程池大小做限制, 线程池大小完全依赖于操作系统(或者说 JVM )能够创建的最大线程大小。
    ( 2) newFixedThreadPool : 创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。 线程池的大小一旦达到最大值就会保持不变, 如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
    ( 3) newSingleThreadExecutor : 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。 如果这个唯一的线程因为异常结束, 那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
    ( 4) newScheduledThreadPool :创建一个定长线程池, 此线程池支持定时以及周期性执行任务的需求。

    (十一)Java中的强引用、软引用、弱引用的区别是什么?

    强引用:是指创建一个对象并把这个对象赋值给一一个引用变量。 强引用不为null时,它指向的对象水远不会被垃圾回收,即使当内存不足时。当强引用被置为nul时,该对象则被标记为可回收的,但是GC可能依旧没有回收它,这和GC的回收算法有关,同时该对象仍然占着内存。总之,我们不能保证可回收的对象被GC回收。
    软引用:通过SoftReference类来实现。软引用指向的对象,不用置null,也可以被GC回收,对象是否被释放取决于GC算法以及GC运行时可用的内存数量。通俗地讲,内存空间足够,GC就不会回收它;如果内存空间不足了,就会回收这些对象的内存。
    弱引用:通过WeakReference类来创建。GC运行时如果碰到了弱引用对象,不管当前内存空间足够与否,都会回收它的内存,但是也有可能需要GC多次才能发现和释放弱引用的对象。
    软引用和弱引用都可以与引用队列( ReferenceQueue)关联,这样就可以知道软引用或者弱引用是否被回收。

    (十二)什么是观察者模式?观察者模式的使用场景是怎样的?观察者模式的优缺点是什么?试编程加以说明。

    观察者模式就是定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
    使用场景:
    (1)关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
    (2)事件多级触发场景。
    (3)跨系统的消息交换场景,如消息队列的处理机制。
    优点:观察者和被观察者之间是抽象耦合;建立了一套触发机制。
    缺点:开发和调试比较复杂,而且一个观察者卡壳,会影响整体的执行效率。同时多级触发时的效率让人担忧。

    (十三)什么是装饰模式?装饰模式的使用场景是怎样的?装饰模式的优缺点是什么?试编程加以说明。

    装饰模式就是动态地给一个对象添加一些额外的职责。
    使用场景:
    (1)需要扩展一个类的功能,或给一个类增加附加功能。
    (2)需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
    (3)需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。
    优点:
    (1)装饰类和被装饰类可以独立发展,而不会相互耦合。
    (2)装饰模式是继承关系的一个替代方案。
    (3)装饰模式可以动态地扩展一个实现类的功能,这不需要多说,装饰模式的定义就是如此。
    缺点:多层类的装饰太过复杂。

    (十四)循环对象 Looper 的作用是什么?请加以说明。

    用于为一个线程开启一个消息队列(MessageQueue),循环等待其他线程发送消息,当有消息时会唤起线程来处理消息,直到线程结束为止。通常情况下Android中并不会为新线程开启消息循环,不会用到Looper,而主线程除外,系统自动为主线程创建一个Looper对象,并创建消息队列,所以主线程会一直运行, 以处理用户事件,直至退出。
    当需要一个线程时,这个线程要能够循环处理其他线程发来的消息事件,或者需要长期与其他线程进行复杂的交互,这时就需要用到Looper来为线程建立消息队列。
    Looper对象提供了以下几个方法:
    ●prepare(): 用于初始化Looper, prepare( )方法保证每个线程至多只有一个Looper对象。
    ●loop(): 用于开启消息循环,当调用了loop( )方法后,Looper线程就真正地开始工作
    了,它会从消息队列中获取消息并交给对应的Handle对象处理消息。
    ●quit(): 用于结束Looper消息循环。

    (十五)Android客户端程序架构的设计一般应考虑哪些问题?(参考教材P374)

    (1)全局变量的定义和使用及全局数据配置;
    (2)推送的实现;
    (3)对新版本的监听和自动下载更新的实现;
    (4)相对完整的基础工具包和核心工具包(类包);
    (5)对客户端各页面单击率的统计及相应数据分析(对用户行为的分析);
    (6)嵌入广告的实现;
    (7) Android 使用UncaughtExceptionHandler捕获全局异常(线程终止异常处理);
    (8) Android系统账户的实现;(9)整个应用的后台服务;
    (10)流量统计;
    (11)整个应用的缓存实现;
    (12)整个应用的内存管理以及内存泄漏的避免;
    (13)整个应用的广播接收机制;
    (14)整个应用的并发设计模式,等等。

    (十六)如何自定义适配器与ListView控件相绑定?扩展BaseAdapter类需要重写哪些方法?试编程加以说明。
    (P40)

    展开全文
  • Android移动应用开发中常见的经验技巧总结 一、对话保持的解决方案。 要求: 1、app中使用webview访问具体网站的内容,但是app与服务器的沟通是使用HttpUrlConnection来完成。 2、webview访问时不需要再次登陆,...
  • 一、课程介绍 英文原文:C# is the best language for mobile app development.Anything you can...中文译意:C#是移动应用程序开发的最佳语言。 在Objective-C,Swift或Java中你可以做的任何事情,你都可以在C#...
  • 通过以下步骤,可以简单了解到如何下载Smobiler Designer(设计器)、Client(客户端),以及如何通过设计器进行开发和调试移动应用,并在服务端部署、Cloud打包、访问您所开发移动应用。   一,设计器下载 ...
  • 基于地理位置服务的Android平台的开发对Android移动开发来说是非常重要的,基于地理位置服务的Android平台的开发是主要用于Android系统作为载体,我们可以利用定位出的位置进行许多丰富多彩的操作。比如说天气预报...
  • Andriod Studio安装与使用
  • 非常有价值的项目架构经验总结: 架构因人而异,不同的架构师大多会有不同的看法;架构也因项目而异,不同的项目需求不同,相应的架构也会不同。然而,有些东西还是通用的,是所有架构师都需要考虑的,也是所有...
  • android项目组件

    2017-01-19 16:17:39
    移动开发里,它将一个大功能整体进行拆分,分别进行单独调试,最后再合并打包 我向大家极力推荐使用组件。因为无论你使用的是何种语言,开发的是什么项目,都有可能面对项目迭代中的各种高度耦合,而且随着开发...
  • Android插件:从入门到放弃 原文地址:http://www.infoq.com/cn/articles/android-plug-ins-from-entry-to-give-up 本文根据包建强在2016GMTC全球移动开发大会上的演讲整理而成。 首先自我介绍一下,我叫...
  • android 项目开发经验总结: Android flash partiong 定制            
  • Android 组件化开发
  • 自学android一段时间了,自我感觉android基础学的差不多了,现在感觉也遇上了一些瓶颈,所以找来一些小项目练练手,实践一下,才知道自己到底会什么,到底不会什么,这篇文章是挺久之前做的一个小游戏——2048的一个...
1 2 3 4 5 ... 20
收藏数 62,779
精华内容 25,111
关键字:

安卓移动开发项目化教程