爆裂吧现实

不想死就早点睡

0%

Bitmap 是内存优化逃不了的一个东西,本文探讨下,Bitmap 中的 density 到底是什么东西,它是如何影响到内存的使用的

先看下 density 的文档注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* <p>Returns the density for this bitmap.</p>
*
* <p>The default density is the same density as the current display,
* unless the current application does not support different screen
* densities in which case it is
* {@link android.util.DisplayMetrics#DENSITY_DEFAULT}. Note that
* compatibility mode is determined by the application that was initially
* loaded into a process -- applications that share the same process should
* all have the same compatibility, or ensure they explicitly set the
* density of their bitmaps appropriately.</p>
*
* @return A scaling factor of the default density or {@link #DENSITY_NONE}
* if the scaling factor is unknown.
*
*/

简单来说 density 是用来绘制缩放用的,默认情况下的 density 就是屏幕的 density(resources.displayMetrics.densityDpi),假如我修改了一张 Bitmap 的 density,那么图片的显示应该会发生缩放,写个简单的 demo 验证下

Read more »

问题件简述

RecycleView 中有 EditText 的时候,上下滚动被复用的时候,发现长按 EditText 没有弹出上下文菜单

分析

调试了一波后发现,正常情况下长按的时候 TextViewperformClick() 函数返回的是 true,没有弹出上下文的时候,此函数返回了 fasle

首先找 TextView 的长按事件 performLongClick(),看其源码

Read more »

某天某时某刻,脑内突然发现一个疑问:RippleDrawable 是怎么把波纹绘制到所在 View 外面的?

稍微了解点 Android 绘制知识的就知道,子 ViewonDraw(canvas) 获取到的画布默认是被父亲裁剪掉的,导致子 View 无法绘制到自身外面

那么问题就来了,为毛 RippleDrawable 可以绘制到外面,用了什么原理?莫非有特权?

Read more »

之前一篇文章,我在 Android 上用 FFmpeg 来压缩视频 (文章链接),对于 exit_program 的处理,是直接改成 return 的。稍微处理不好一点(因为 c 没有异常),程序就会 creash。还有就是在 Android 上,其实可以使用 MediaCodec 来硬解码视频的。本文讲述怎么解决上面两个问题。

Read more »

前几天项目需要压缩视频,Github上找了许多库,要么就是太大,要么就是质量不高,其实我只需要压缩视频,最好的方案还是定制编译一个 FFmpegAndroid 用。

本项目使用 FFmpeglibx264(一个第三方的视频编码器) 来编译出可以在 Android 上使用的动态库

Read more »

用过 iOS 的都知道,拟物理的回弹效果在上面非常普遍,因为这是 iOS 系统支持的一套 UI 框架,但是 Android 就没有了,就拿图片查看器来讲,iOS 的效果就是感觉一张图片被绑定在了弹簧装置上,滑动很自然,Android 没有自带的图片查看器,需要自己实现

市面上主流的图片查看器都没有回弹的效果,一部分原因是没有这个需求,还有一部分是实现麻烦,这里讲述一个个人认为最好的方案

需求

一个图片查看器,要求可以滑动 Fling,触碰到边界的时候回弹,有越界回弹的效果,支持双指缩放,双击缩放

分析

咋一看需求,应该好写,滚动的时候用 Scroller 来解决,回弹效果直接用 ValueAnimator,设置插值器为减速插值器来解决。看似简单,但是因为是仿物理效果,中间牵扯到从滚动到回弹的时候(Scroller动画切换到ValueAnimator动画)的速度衔接问题,要看上去从滚动到开始回弹至结束没有突兀,中间的特判边界处理是很麻烦的,还要牵扯到缩放,所以不考虑这种方案

既然是要模拟现实中的物理效果,为何不在每一帧根据当前的状态得到对用的加速度,然后去计算下一帧的状态位置,这样只要模拟现实中的物理加速度不就可以实现了吗,那些边界特判之类的就可以去见阎王了

方案确定完毕,接下来就是选定加速度的方程,要模拟弹簧的效果,拉力很简单,用胡克定律嘛!F = k * dx,摩擦力呢?Ff = μ*FN ? 这里推荐一个更加好的方案,借鉴自 Rebound 库,这是 Facebook 的一个弹簧动画库,设定一个目的数值,它会根据当前的拉力,摩擦力,速度然后变化到目标值,加速度方程为

$$
\vec a=tension\cdot\vec {dx} - friction\cdot\vec v
$$

其中 tension 为弹性系数,friction为摩擦力系数,为什么让摩擦力和速度成正比呢?如果摩擦力和速度成正比,那么就不存在静摩擦力,也就是不存在物体静止情况下拉力小于摩擦力的情况(因为速度为0的时候,阻力为0,除非拉力为0),物体肯定会向目标地点靠近,遏制了物体摩擦力过大而无法达到目的地情况

Read more »

iOS系统自带了模糊效果,但是Android却没有,网上有很多实现方式,基本上都是用 faseBlur 算法,或者使用 RenderScript。

本文用的也是这两种方法,当 api < 17 的时候用 faseBlur 算法,当 api >= 17 的时候用 RenderScript。不同点是,网上基本都是通过获取一张和视图大小相同的 Bitmap 然后压缩、模糊。现在手机的屏幕分辨率都很高,如果使用这种方法的话,第一次创建的 Bitmap 将会占用很大一部分内存,很容易出现 OOM 现象。因为我们做模糊处理,所以只需要一张视图的缩略图即可,不需要一张高清的图片,有没有办法获取到 View 的缩略图呢?答案是有的。

一般获取 ViewBitmap 用的是 ViewgetDrawingCache 这个方法,此方法返回的是一张和 View 大小一模一样的位图。所以我们需要自己写获取位图的方法,参考 getDrawingCache,里面是通过 view.draw(canvas)在一张位图上画出视图的。这个就简单了,我们可以创建一个小型的 Bitmap 然后通过设置 Canvas 的缩放在这张小画布上画出视图的缩略图,这样子即省内存,也省时间(原先压缩需要时间)。

Read more »

2016-08-10 更新

添加毛玻璃背景效果,模糊内存优化,详见 快速模糊效果

iOS上的chrome中有侧滑返回上一个页面的功能,感觉蛮好用的,刚好Android没有自带的侧滑返回效果,如果使用透明的Activity的话比较浪费性能,所以打算在实现一个简单的DragBackActivity,拖动的效果模仿iOS上的Chrome侧滑返回。

效果图如下:
Alt text

使用方法:
继承自DragBackActivity就可以有侧滑返回效果。
如果想要禁用,重写isDisableDrag()函数返回true
定制返回动画的色彩:

1
2
3
4
5
6
//图标色,变化前
mDragLayer.setPositiveColor(...);
//图标色,变化后
mDragLayer.setNegativeColor(...);
//圆圈色
mDragLayer.setCircleColor(...);

粗略解析:

因为需要实现的目的是继承自这个DragBackActivity就可以实现拖动返回的效果,因为是靠近边缘的侧滑返回,所以要用到手势处理,需要获取到当前Activity的根视图,手势的处理是要放在视图层处理的。

每个Activity都有一个idandroid.R.id.content的根视图,setContentView所设置的View就是该根视图的子View,本项目就是根据这个特性来移动整个Acivity的。

知道怎么移动Activity了,接下来就只剩拦截手势和绘制返回动画了。Android的事件传递是根据整棵视图树来传递的,所以视图越靠近树的根就越先收到触摸事件。所以我需要在android.R.id.content上或者同级的地方添加自定义的View,这时候就需要Window出场了,window有一个方法是获取到当前窗口的根视图:

1
2
//此函数返回的是view 需要强转成view group
getWindow().getDecorView();

获取到窗口的根视图之后就可以往上面添加自定义视图了,手势拦截处理写在自定义视图中。废话不多说下面来看源码。

Read more »

此文章紧接事件分发机制源码解析(一),建议顺序翻阅。

当正在分发事件的时候(_inDispath > 0),添加和删除监听器事件

EventDispatcher 中有表示当前分发的事件数的私有成员变量 _inDispatch ,它是一个 int 类型的数据,用于表示当前正有多少事件正在分发。既然是表示事件正在分发的数量,可定有 ++, –  的操作,在DispatchEvent中会有+1和-1的操作,但是藏得比较深,怎么个深法,看下述源码。  

1
2
3
4
5
6
7
8
void EventDispatcher::dispatchEvent(Event* event)
{
    ...
 
    DispatchGuard guard(_inDispatch);
 
    ...
}

 
我去,这啥都都没有啊???发现这里面只有使用 _inDispatch 创建了一个变量,并没有++,- -操作。其实 DispatchGuard 这个类的构造函数的参数是一个 int 类型的引用,构造时候对其+1,析构函数的时候会-1操作。用法很妙,借助了局部变量是在栈里面这个特性,当方法DispatchEvent结束的时候,局部变量guard会析构,此时会-1操作。下面是 DispatchGuard 的源码。  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class DispatchGuard
{
public:
    DispatchGuard(int& count):
            _count(count)
    {
        ++_count;
    }
 
    ~DispatchGuard()
    {
        --_count;
    }
 
private:
    int& _count;
};
Read more »

Cocos的事件分发机制,怎么说呢,总感觉有些乱,借此整理一下。先看看与事件分发相关的类。  

事件相关的类

Event 相关类, 分发事件的中的事件:

Event(基类), EventCustom(自定义事件), EventTouch(触摸事件), EventMouse(鼠标事件), EventKeyboard(键盘事件), EventFocus(控件获取焦点事件), EventAcceleration(加速计事件)

事件监听器:

EventListener, EventListenerCustom, EventListenerFocus, EventListenerMouse, EventListenerTouch, EventListenerKayboard, EventListenerAcceleration

事件ID

ListenerID(事件的区分标志,其实就是std::string

事件分发器:

EventDispatcher(事件分发机制逻辑集合体)

创建事件

创建事件就简单的new一个Event的子类即可。  

事件监听器的创建与监听

事件监听器,也就是说EventListener。添加事件监听器有三个方法,都在EventDispatch中,分别是:  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/** Adds a event listener for a specified event with the priority of scene graph.
 *  @param listener The listener of a specified event.
 *  @param node The priority of the listener is based on the draw order of this node.
 *  @note  The priority of scene graph will be fixed value 0. So the order of listener item
 *          in the vector will be ' <0, scene graph (0 priority), >0'.
*/
void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node);

/** Adds a event listener for a specified event with the fixed priority.
 *  @param listener The listener of a specified event.
 *  @param fixedPriority The fixed priority of the listener.
 *  @note A lower priority will be called before the ones that have a higher value.
 *        0 priority is forbidden for fixed priority since it's used for scene graph based priority.
*/
void addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);
 
/** Adds a Custom event listener. It will use a fixed priority of 1.
* @param eventName A given name of the event.
* @param callback A given callback method that associated the event name.
* @return the generated event. Needed in order to remove the event from the dispatcher
*/
EventListenerCustom* addCustomEventListener(const std::string &eventName, const std::function<void(EventCustom*)>& callback);

 
事件分发的时候主要有两种优先级
    第一种是:ScenePriority
    第二种是:FixedPriority
仔细跳到addEventListenerWithSceneGraphPriority函数去看看会发现:  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
{
    ...
    // 设置优先级
    listener->setFixedPriority(0);
    ...
 
    addEventListener(listener);
}
 
EventListenerCustom* EventDispatcher::addCustomEventListener(const std::string &eventName, const std::function<void(EventCustom*)>& callback)
{
    EventListenerCustom *listener = EventListenerCustom::create(eventName, callback);\
    // 设置优先级
    addEventListenerWithFixedPriority(listener, 1);
    return listener;
}

Read more »