荏苒追寻个人博客

做一个有追求的青年


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 日程表

Android 开源库分析——ExoPlayer封装的音视频播放器

发表于 2023-10-13 | 分类于 Android , 开源库分析

本文链接:https://rainmonth.github.io/posts/A191224.html

摘要

功能

需要有以下功能

  • 要有良好的可移植性
  • 基本的播放控制(播放、暂停、拖动、播放状态判断、播放模式选择)
  • 支持列表播放

Android 开源库分析——ExoPlayer的Timeline

发表于 2023-10-13 | 分类于 Android , 开源库分析

本文链接:https://rainmonth.github.io/posts/A191130.html

摘要

刚开始分析ExoPlayer源码时,对于Timeline的概念不是很理解,这几天重新看了下,这里对Timeline及其相关概念做个简单分析

Timeline

Timeline是一种数据结构,可以灵活的代表各种媒体的结构,Timeline的实例不可修改,对于直播流媒体,Timeline提供的是当前状态的快照。

Timeline由Timeline.Period和Timeline.Window组成。Period是Media逻辑上的代表(可以是单个的媒体文件、也可以是包含掺入广告的播放信息的插入广告的媒体文件)。Window可以横跨多个Period,定义了默认的播放开始位置,所在Period可以播放的范围。

阅读全文 »

Android 开源库分析——IjkPlayer简介

发表于 2023-10-13 | 分类于 Android

本文链接:https://rainmonth.github.io/posts/A191206.html

摘要

结合源码,分析下ijkplayer的初始化流程,并对这个过程中的一些关键点(如消息循环、线程创建C层和Java层的通信几个方面来)做了一个详细分析。

阅读全文 »

Android 基础——Canvas

发表于 2023-10-13 | 分类于 Android

https://rainmonth.github.io/posts/A220131.html

  • Canvas drawText() 文本居中

自定义View永远逃不了一个话题,就时View的绘制,而View的绘制就逃不了要同今天的主角——Canvas来打交道。

Canvas控制着view绘制相关的所有调用,也就是说,view上显示的元素都需要通过Canvas调用形影的drawXX方法来绘制出来,当然要想完成View的绘制,需要一下四要素:

  1. 一个Bitmap来存储绘制的像素对象;
  2. 一个Canvas来控制draw相关方法调用(将内容像素绘制到1中的Bitmap中去);
  3. 一个基础的绘制元素(比如Rect、text、Path、Bitmap等);
  4. 用来进行绘制的画笔Paint;

基本概念

绘制操作

Canvas可以通过一系列的drawXXX方法来进行基本元素的绘制,下面进行简单说明:

  • drawArc,画弧;
  • drawARGB,将Canvas内部维护的multable Bitmap涂色;
  • drawBitmap, 绘制Bitmap;
  • drawCircle,绘制圆形;
  • drawColor,以指定的混合模式为Canvas涂色(默认SRC_OVER混合模式);
  • drawline(s),绘制线段或者线段组;
  • drawOval,绘制椭圆;
  • drawPaint,用指定的画笔填充 Canvas内部的Bitmap,等同于用画笔绘制一个Canvas大小的矩形,但是drawPaint效率更高;
  • drawPatch,绘制.9图片;
  • drawPath,绘制路径,非常重要;
  • drawPoint(s),绘制一个或多个点;

变换操作

Canvas的变化操作主要包括:平移、缩放、旋转、错切,分别对应一下方法:

  • translate(float dx, float dy),canvas上的对象X、Y分别都平移 dx 和 dy;
  • scale(float sx, float sy),canvas上的对象X、Y分别都乘以 sx 和 sy;
  • rotate(float degrees, float px, float py)
  • skew(float x, float y)
  • concat(Matrix matrix),将matrix 矩阵变换应用到 canvas的所有对象上;

这里顺便说下Matrix

Matrix是一个3*3的矩阵(如何在Markdown中表示矩阵),

矩阵各个值的含义:

  • scaleX(Y)、skewX(Y)、transX(Y),分别代表X、Y轴上相应的 scale(缩放)、skew(错切)、translate(平移)操作;

  • persp0、persp1、persp2 这三个值和透视有关,一般很少用到;

注意:

  1. 如果需要 通过矩阵 进行旋转造作,需要同时修改scaleX、skewX、scaleY、skewY。

  2. pre开头的表示要进行 矩阵的 左乘, post开头的表示要进行 矩阵的 右乘

状态栈管理操作

clip操作

参考文章

  1. 通用功能——矩阵的操作(todo);

  2. 通用功能——Markdown常用格式说明;

Android基础——View 知识点

发表于 2023-10-13 | 分类于 Android , 杂谈

https://rainmonth.github.io/posts/A210219.html

View 大小相关

getWidth()与getMeasuredWidth()的区别

View 的width和height、measuredWidth和measuredHeight的意义,分别的使用场景和赋值时机?

getWidth()代码:

1
2
3
public final int getWidth() {
return mRight - mLeft;
}

getMeasuredWidth()代码:

1
2
3
4
public final int getMeasuredWidth() {
// mMeasuredWidth在onMeasure调用过程中赋值
return mMeasuredWidth & MEASURED_SIZE_MASK;
}
  • getWidth()的值在layout(int l, int t, int r, int b)执行完成后真正确定;
  • getMeasureWidth()的值是在onMeasure(int widthMeasureSpec, int heightMeasureSpec)中的setMeasuredDimension(int measureWidth, int measrueHeight)执行完成后确定的,mMeasureWidth本身是包含测量模式和测量的具体值的,但在调用getMeasureWidth()后,进行了掩码操作,将高两位移除了;

结论:子View最终的位置是在onLayout 中确定的,但子View的宽高是在onMeasure中确定的

MeasureSpec相关

到底是个啥?各种值的含义?mode和size分别代表什么意思?

MeasureSpec详解

MeasureSpec表示的是View的测量结果,用一个int值表示(int值四个字节)其中高两位用来表示mode(测量模式),低30位用来表示size(测量的值),测量模式有三种:

  • UNSPECIFIED:不对View大小做限制,如:ListView,ScrollView
  • EXACTLY:确切的大小,如:100dp或者march_parent
  • AT_MOST:大小不可超过某数值,如:wrap_content

着重看一下ViewGroup的getChildMeasureSpec(int spec, int padding, int childDimension)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/**
* @param spec The requirements for this view
* @param padding padding
* @param childDimension How big the child wants to be in the current
* dimension
*/
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);

int size = Math.max(0, specSize - padding);

int resultSize = 0;
int resultMode = 0;

switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;

// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;

// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

针对以上ViewGroup的getChildMeasureSpec代码做个小结:

  1. 不管父View是何模式,若子View有确切数值,则子View大小就是其本身大小,且mode是EXACTLY
  2. 若子View是match_parent,则模式与父View相同,且大小同父View(若父View是UNSPECIFIED,则子View大小为0)
  3. 若子View是wrap_content,则模式是AT_MOST,大小同父View,表示不可超过父View大小(若父View是UNSPECIFIED,则子View大小为0)
  1. View的一般绘制流程是什么?

    measure->layout->draw

  2. 源码分析自定义ViewGroup onDraw方法无效

事件分发

事件分发机制(参考一文读懂Android View事件分发机制)

1
2
3
4
5
6
7
8
9
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;//事件是否被消费
if (onInterceptTouchEvent(ev)){//调用onInterceptTouchEvent判断是否拦截事件
consume = onTouchEvent(ev);//如果拦截则调用自身的onTouchEvent方法
} else{
consume = child.dispatchTouchEvent(ev);//不拦截调用子View的dispatchTouchEvent方法
}
return consume;//返回值表示事件是否被消费,true事件终止,false调用父View的onTouchEvent方法
}
  1. RecyclerView中嵌套横向的RecyclerView,外层RecyclerView竖直滑动时,内层的不能保持原来的位置
  2. RecyclerView的PagerSnapHelper的用法
  3. ViewPager直接调用setCurrentItem,不触发ViewPager.OnPageChangeListener的onPageScrollStateChanged,但触发onPageSelected
  4. 子View最终的位置是在onLayout 中确定的,但子View的宽高是在onMeasure中确定的
  5. ViewPager的PageTransform效果在调用Adapter的notifyDataChanged方法后会失效,需要重新setAdapter
  6. ViewPager中显示单独的View时,调用notifyDataChange时View里面的内容不会更,原因是ViewPager的Adapter被设计成在其显示的Item增加删除时起作用,即instantiateItem和destroyItem两个方法

这个时候可以重写Adapter的getItemPostion方法,让其返回POSITION_NONE(该方法默认放回UNCHANGED,但这个时候需要自己解决显示View列表的缓存问题

如果不解决,那么ViewPager中的View就会越来越多

Unity基础——使用原生的基本物体创建项目

发表于 2023-08-07 | 分类于 Unity

Unity Protype2学习记录

发表于 2023-08-05 | 分类于 Unity

中级难度:动物的横向与纵向生产

先看看横向生成的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using UnityEngine;

public class SpawnManager : MonoBehaviour
{
public GameObject[] animalPrefabs;

private float spawnRangeX = 20;
private float spawnPosZ = 20;

private float startDelay = 2;
private float spawnInterval = 1.5f;

//public int animalIndex;

// Start is called before the first frame update
void Start()
{
InvokeRepeating("SpawnRandomAnimal", startDelay, spawnInterval);
}

// Update is called once per frame
void Update()
{
//if (Input.GetKeyDown(KeyCode.S))
//{
//if (animalIndex < 0)
//{
// animalIndex = 0;
//}
//else if (animalIndex >= animalPrefabs.Length)
//{
// animalIndex = animalPrefabs.Length - 1;
//}
//SpawnRandomAnimal();
//}
}

/**
* 随机生成动物
*/
private void SpawnRandomAnimal()
{
int animalIndex = Random.Range(0, animalPrefabs.Length);
Vector3 spawnPos = new Vector3(Random.Range(-spawnRangeX, spawnRangeX), 0, spawnPosZ);
Instantiate(animalPrefabs[animalIndex], spawnPos, animalPrefabs[animalIndex].transform.rotation);
}
}

游戏是在XZ这个坐标平面上的,X轴是横向的,Z轴是纵向的,所以你纵向生产的位置由spawnPosZ决定,纵向生成范围由spawnRangeX决定。类似的,如果要支持横向生成,那么要定义一下子两个类型的变量:

  1. 横向生成动物的范围,这个左右两边可以共用一个;
  2. 横向生成动物的位置,这个左右两边的需要分别定义,因为值不同;

问题点:

  1. 增加了Rigidbody属性后的Game Object,需要判断是否需要使用它的Gravity属性;
  2. 如果在运行时发现不对,效果不符合预期,记得切换下相机视角;
  3. 碰撞体检测问题:子弹由玩家发射,子弹存在碰撞检测,敌人也存在检测,所以要区别好子弹碰撞到玩家和敌人碰撞到玩家。可以在玩家Game Object上建一个子物体,该问题用于给子弹进行位置生成。

困难难度:添加游戏交互逻辑

简单的交互玩法:

  1. 定义一下玩家的初始生命值为3,初始分数为0;
  2. 动物超出边界前没有被投喂,则玩家生命值-1,投喂到动物,则分数加5;

Unity官方教程7——3D Game Kit学习记录

发表于 2023-08-03 | 分类于 Unity

官方教程地址:3D Game Kit

创建场景

资源包中,提供了一个菜单Kit Tools,点击该菜单,然后选择Create New Scece就可以创建一个包含了主角Ellen和地面的简单场景了。

注意:Unity可以自定义编辑器,官方出的Game Kit,都通过代码方式,对编辑器进行了修改,添加了好多功能菜单,这就是为什么这个3D Game Kit为什么说不用编码也能开始游戏设计的原因,因为需要编码的部分都被“组件化”了,只需要利用已经封装好的功能

可以通过WASD控制角色的移动,空格控制跳跃,鼠标控制摄像机的视角,这里需要分析下这里的代码实现:

1
2
3
4
1. WASD控制角色的移动很好实现,通过InputManager的GetAxis来获取相应的轴,然后进行位移即可;
2. 空格键控制跳跃,同样也是捕获空格键按下事件,然后进行跳跃相关操作即可。跳跃涉及到一下几点:
2.1 跳跃过程中的位移实现(需进一步了解);
2.2 跳跃过程中角色的动画实现(需进一步了解);

使用预制件

Game Kit提供了很多预制件,有可交互的,也有不可交互的,对于这些预制件,可以拖动到场景中,然后看看他都有哪些Component组成,在具体看看哪些脚本组件的具体实现代码。

ProBuilder的使用

ProBuilder是Unity自带的建模工具。参考链接:

  1. Unity官方ProBuilder介绍
  2. Unity建模工具ProBuilder,可提引擎内实现白盒关卡

PolyBrush的使用

Android 控件——一个示例展示View的事件处理机制

发表于 2023-04-07 | 分类于 Android

Flutter——Android开发基础

发表于 2023-03-30 | 分类于 Flutter

Flutter——Android开发基础

先看看官方给Android开发者的指南

  • 给 Android 开发者的 Flutter 指南
  • 声明式UI介绍
  • Dart概览
<i class="fa fa-angle-left" aria-label="上一页"></i>1…456…22<i class="fa fa-angle-right" aria-label="下一页"></i>

216 日志
43 分类
43 标签
GitHub
© 2025 Randy Zhang
由 Hexo 强力驱动
|
主题 — NexT.Gemini v6.1.0