当前位置: 首页 > news >正文

国外直播做游戏视频网站网店搜索引擎优化的方法

国外直播做游戏视频网站,网店搜索引擎优化的方法,如何策划网络推广方案,南乐网站开发每当设计稿上注明需要添加阴影时,Android上总是显得比较棘手,因为Android的阴影实现方式与Web和iOS有所区别。 一般来说阴影通常格式是有: X: 在X轴的偏移度 Y: 在Y轴偏移度 Blur: 阴影的模糊半径 Color: 阴影的颜色 何为阴影 但是在A…

每当设计稿上注明需要添加阴影时,Android上总是显得比较棘手,因为Android的阴影实现方式与Web和iOS有所区别。

一般来说阴影通常格式是有:

X: 在X轴的偏移度

Y: 在Y轴偏移度

Blur: 阴影的模糊半径

Color: 阴影的颜色

何为阴影

但是在Android中却比较单一,只有一个度量单位:Elevation,作为在Android5.0的material2引入的概念,用一个图来形象的描绘一下,其实本质上就是虚拟的Z轴坐标。

image.png

那好,高度差有了,还差个光源,这样才能形成阴影,在material2中,光源不是单一的位于屏幕正上方的,而且有两组光源,分为主光源(Key light)和环境光源(Ambient light)如下图所示:
image.png

最终形成的效果是一种复合光源下更自然的阴影。

image.png

其中环境光源,在屏幕空间中没有实际的位置,但是主光源是有实际的位置的,具体的参数见:

frameworks/base/core/res/res/values/dimens.xml - Android Code Search
image.png

好,既然知道了阴影本身的机制,那下一步现在则是如何自定义控制阴影,这也是本文的目的。

从SDK 21开始,提供了Elevation可以实现类似于阴影的模糊半径的效果,但是毕竟尺度过于单一,往往有时候无法满足所需的效果,所以,还需要控制阴影的颜色。

在SDK 28之后,可以通过outlineSpotShadowColoroutlineAmbientShadowColor来分别设置Key light和Ambient light投射的阴影颜色,但是说实话,这两个属性基本用不到或者说比较鸡肋。

不过这里引入了一个概念:Outline。

四种常见方案

Elevation + Outline

Outline其实是View的边框(轮廓),通过OutlineProvider可以自定义一个View的Outline从而影响View本身在elevation下的投影,比如定义以实现一个圆角ImageView为例:

<ImageViewandroid:id="@+id/image"android:layout_width="100dp"android:layout_height="100dp"android:src="@color/material_dynamic_primary90" />
image.clipToOutline = true
image.outlineProvider = object : ViewOutlineProvider() {override fun getOutline(view: View?, outline: Outline?) {view ?: returnoutline?.setRoundRect(0, 0, view.width, view.height, 32f)}}

效果基本没啥问题:
image.png

同样的,既然View的轮廓变化了,阴影自然也会跟着随之变化,所以outline也可以改变阴影:

image.elevation = 32f
image.outlineAmbientShadowColor = Color.RED
image.outlineSpotShadowColor = Color.BLUE
image.clipToOutline = true
image.outlineProvider = object : ViewOutlineProvider() {override fun getOutline(view: View?, outline: Outline?) {view ?: returnoutline?.setRoundRect(0, 0, view.width, view.height, 32f)}}

效果如下:(不过outlineAmbientShadowColoroutlineSpotShadowColor仅支持SDK 28及以上)

image.png

通常,到这一步通过调整elevation的数值和outline以及高版本可用的shadowColor大体上可以满足设计师的阴影需求。
而且通常来说shadowColor都是Color.Black以及alpha的区别,所以你也可以这样:

outlineProvider = object : ViewOutlineProvider() {override fun getOutline(view: View?, outline: Outline?) {view ?: returnoutline?.alpha = 0.5foutline?.setRoundRect(0, 0, view.width, view.height, 32f)}
}

但是,还记着前面提到的两个光源吗?其中有一个光源是位于屏幕斜上方的,这就带来了另外一个问题,同一个View设置相同的Elevation在不同的Y轴坐标它的阴影效果是不一样的,如下图所示:

总之,阴影的Blur和Color参数勉强是可以得到满足的。

优点:原生的阴影效果

缺点:设置阴影的颜色需要SDK>=28,需要配合使用outline来实现对阴影的轮廓控制

下面我们先来引申一下Android中了解过的阴影实现方式。

LayerDrawable

我相信大家肯定见过这种实现方式,通过绘制一层层渐变色来模拟阴影,其实官方也有通过该方式实现的阴影:MaterialShapeDrawable,示例如下:

val drawable = MaterialShapeDrawable(ShapeAppearanceModel.builder().setAllCornerSizes(16.dp).build()
)
drawable.fillColor = ColorStateList.valueOf(getColor(com.google.android.material.R.color.material_dynamic_primary90))
drawable.setShadowColor(Color.RED)
drawable.shadowVerticalOffset = 8.dp.toInt()
drawable.elevation = 32f
drawable.shadowCompatibilityMode = MaterialShapeDrawable.SHADOW_COMPAT_MODE_ALWAYS
image.background = drawable

效果如图:
image.png

只能说很一般,毕竟是模拟的阴影模糊效果,而且目前只支持Y轴的offset。

优点:几乎是开箱即用的Drawable且自带圆角

缺点:模拟的阴影效果,展示效果不够精细且效率不高

NinePatchDrawable

说实话想在Android上实现一个简单的阴影太折腾了,什么奇怪的技巧都来了,比如.9图,至于什么是.9图这里便不再过多介绍。
通过这个网站:Android Shadow Generator (inloop.github.io)

image.png
你可以直接生成一个CSS Style的阴影效果,几乎可以完美还原Figma的阴影效果,效果如下:
image.png

其实还是很还原的,但是它有一个致命的缺点,就是圆角,因为是一张图片,所以圆角的单位本质上是px而非Android上的dp,如果你需要一个带圆角弧度的阴影是达不到预期的。

优点:参数完全可控的阴影,可以做到1:1还原设计稿

缺点:因为是图片,所以阴影的圆角无法跟随像素密度缩放(非常致命的缺点)

Paint.setShadowLayer/BlurMaskFilter

这两个我之所以放在一起本质上是因为实现起来都是类似的,
如:

paint.setShadowLayer(radius, offsetX, offsetY, shadowColor)
// 或者使用maskFilter然后通过paint.color以及绘制的区域进行offset来变相控制阴影的Color及offset
paint.maskFilter = BlurMaskFilter(field, BlurMaskFilter.Blur.NORMAL)

相比之下更推荐使用setShadowLayer,最终效果如下,基本上没啥问题:
image.png

但是值得注意的是,其绘制的阴影本质上等价于BlurMaskFilter,是占位的,而且是需要留出空间来展示的,所以必要时需要对父布局设置android:clipChildren="false"或者预留出足够的空间。

优点:

1. 参数完全可控的阴影,可以做到1:1还原设计稿

2. 参数的自定义程度及可控性强

缺点:

1. 阴影占位,需要通过clipChildren=false来或者预留空间规避

2. 需要自定义View或者Drawable,写起来较为麻烦。

总的来说,上面介绍了4种可能常见的阴影实现方式,其中按我的经验来说,较为推荐采用Outline或者setShadowLayer的方式来实现,如果可以的话原生Elevation配合Outline基本可以满足大部分需求场景。

当然还有部分实现方式比如用RenderScriptBlur等等,我没提是因为是前几种方式较为复杂,性价比不高。

Paint.setShadowLayer 扩展内容

下面则重点讲一下Paint.setShadowLayer/BlurMaskFilter这种方式,为什么说这两种方式实现的阴影都是一致的呢?这个就需要深入到C++层。
首先直接跳到paint.setShadowLayer的native实现类:
frameworks/base/libs/hwui/jni/Paint.cpp

Paint.cpp - Android Code Search

    static void setShadowLayer(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jfloat radius,jfloat dx, jfloat dy, jlong colorSpaceHandle,jlong colorLong) {SkColor4f color = GraphicsJNI::convertColorLong(colorLong);sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);Paint* paint = reinterpret_cast<Paint*>(paintHandle);if (radius <= 0) {paint->setLooper(nullptr);}else {SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);paint->setLooper(BlurDrawLooper::Make(color, cs.get(), sigma, {dx, dy}));}}

里面将我们传入的阴影radius参数转为Sigma并创建了BlurDrawLooper,我们来看看其实现

#include "BlurDrawLooper.h"
#include <SkMaskFilter.h>namespace android {BlurDrawLooper::BlurDrawLooper(SkColor4f color, float blurSigma, SkPoint offset): mColor(color), mBlurSigma(blurSigma), mOffset(offset) {}BlurDrawLooper::~BlurDrawLooper() = default;SkPoint BlurDrawLooper::apply(Paint* paint) const {paint->setColor(mColor);if (mBlurSigma > 0) {paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, mBlurSigma, true));}return mOffset;
}sk_sp<BlurDrawLooper> BlurDrawLooper::Make(SkColor4f color, SkColorSpace* cs, float blurSigma,SkPoint offset) {if (cs) {SkPaint tmp;tmp.setColor(color, cs);  // converts color to sRGBcolor = tmp.getColor4f();}return sk_sp<BlurDrawLooper>(new BlurDrawLooper(color, blurSigma, offset));
}}  // namespace android

内容不多,可以看到本质上还是利用了setMaskFilter来实现的。

然后还剩下一个点就是通过SkMaskFilter::MakeBlur生成的模糊是占位的,如果能知道模糊具体需要多大的空间,就可以方便的进行预留以免实际展示时阴影被裁剪。
MakeBlur最终返回的是一个SkBlurMaskFilterImpl对象,我们可以先看一下其父类SkMaskFilterBase的虚函数:重点关注computeFastBounds函数

SkMaskFilterBase.h - Android Code Search

    /*** The fast bounds function is used to enable the paint to be culled early* in the drawing pipeline. This function accepts the current bounds of the* paint as its src param and the filter adjust those bounds using its* current mask and returns the result using the dest param. Callers are* allowed to provide the same struct for both src and dest so each* implementation must accommodate that behavior.**  The default impl calls filterMask with the src mask having no image,*  but subclasses may override this if they can compute the rect faster.*/virtual void computeFastBounds(const SkRect& src, SkRect* dest) const;

可以看到该函数的作用便是计算MaskFiter的bounds,看一下子类的SkBlurMaskFilterImpl的实现

void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,SkRect* dst) const {// TODO: if we're doing kInner blur, should we return a different outset?//       i.e. pad == 0 ?SkScalar pad = 3.0f * fSigma;dst->setLTRB(src.fLeft  - pad, src.fTop    - pad,src.fRight + pad, src.fBottom + pad);
}

其中fSigme便是最开始通过convertRadiusToSigma(radius)获取到的返回值,其计算方式如下:
SkBlurMask.cpp - Android Code Search

// This constant approximates the scaling done in the software path's
// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
// IMHO, it actually should be 1:  we blur "less" than we should do
// according to the CSS and canvas specs, simply because Safari does the same.
// Firefox used to do the same too, until 4.0 where they fixed it.  So at some
// point we should probably get rid of these scaling constants and rebaseline
// all the blur tests.
static const SkScalar kBLUR_SIGMA_SCALE = 0.57735f;SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) {return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
}

这样,我们可以得到一个模糊的近似Bound,虽然不是一个准确的值但是至少可以保证绘制的阴影不会被裁剪。
当然,如果无法预留Padding也可以通过clipChildren=false来实现。

总结

最后我也是针对setShadowLayer提供了一个自定义View的实现方式:

Lowae/Shadows: A simple and customizable library on Android to implement CSS style shadows (github.com)

感兴趣的可以尝试使用,有任何兼容性问题欢迎提issue~

(我十分清楚会有很多兼容性问题,没办法,这种Api就是这样,不,准确来说,Android就是这样)

所以,想在Android上1:1还原设计稿上的阴影是比较困难的,但是如果不去追求参数的还原只是寻求视觉的略显一致,那还是可以做到的,简单点的通过第一种方式(Elevation + Outline),如果设置到阴影颜色或者offset这种便可以尝试最后一种方式(setShadowLayer)。


文章转载自:
http://portocaval.jjpk.cn
http://menopausic.jjpk.cn
http://spinose.jjpk.cn
http://busily.jjpk.cn
http://aganippe.jjpk.cn
http://stellate.jjpk.cn
http://etape.jjpk.cn
http://momentous.jjpk.cn
http://consolidate.jjpk.cn
http://ishmaelite.jjpk.cn
http://noachian.jjpk.cn
http://okie.jjpk.cn
http://anthologize.jjpk.cn
http://potash.jjpk.cn
http://acculturate.jjpk.cn
http://myrna.jjpk.cn
http://waggish.jjpk.cn
http://racemiferous.jjpk.cn
http://graphicacy.jjpk.cn
http://ultracentenarian.jjpk.cn
http://chd.jjpk.cn
http://prepend.jjpk.cn
http://fragmentation.jjpk.cn
http://guid.jjpk.cn
http://bushwalking.jjpk.cn
http://winter.jjpk.cn
http://twayblade.jjpk.cn
http://pigeonhole.jjpk.cn
http://diagnosticate.jjpk.cn
http://cardialgia.jjpk.cn
http://siloxane.jjpk.cn
http://deadwork.jjpk.cn
http://heterocyclic.jjpk.cn
http://edmond.jjpk.cn
http://doukhobors.jjpk.cn
http://unenjoying.jjpk.cn
http://sephadex.jjpk.cn
http://significs.jjpk.cn
http://phlegm.jjpk.cn
http://centaurae.jjpk.cn
http://sacher.jjpk.cn
http://uncap.jjpk.cn
http://cryptanalyst.jjpk.cn
http://snakelet.jjpk.cn
http://decalage.jjpk.cn
http://suggest.jjpk.cn
http://pyrophyllite.jjpk.cn
http://lavash.jjpk.cn
http://gasogene.jjpk.cn
http://enunciability.jjpk.cn
http://appalling.jjpk.cn
http://solgel.jjpk.cn
http://thru.jjpk.cn
http://bunchberry.jjpk.cn
http://clambake.jjpk.cn
http://pimpmobile.jjpk.cn
http://tubiform.jjpk.cn
http://trm.jjpk.cn
http://cephalothorax.jjpk.cn
http://opposite.jjpk.cn
http://emetic.jjpk.cn
http://continua.jjpk.cn
http://okhotsk.jjpk.cn
http://osteometrical.jjpk.cn
http://pepo.jjpk.cn
http://phocine.jjpk.cn
http://overcanopy.jjpk.cn
http://pinwork.jjpk.cn
http://scatophagous.jjpk.cn
http://encoffin.jjpk.cn
http://metallogenetic.jjpk.cn
http://spondee.jjpk.cn
http://racemiform.jjpk.cn
http://heliotype.jjpk.cn
http://filings.jjpk.cn
http://undiscipline.jjpk.cn
http://directly.jjpk.cn
http://vertigo.jjpk.cn
http://screwed.jjpk.cn
http://amplificatory.jjpk.cn
http://agnosia.jjpk.cn
http://brinjaul.jjpk.cn
http://worktable.jjpk.cn
http://preclusive.jjpk.cn
http://republicrat.jjpk.cn
http://hypnology.jjpk.cn
http://riskless.jjpk.cn
http://confessant.jjpk.cn
http://inbreeding.jjpk.cn
http://chemoreceptor.jjpk.cn
http://dartist.jjpk.cn
http://defensibly.jjpk.cn
http://sesterce.jjpk.cn
http://mendable.jjpk.cn
http://unmuzzle.jjpk.cn
http://adidas.jjpk.cn
http://diplomatise.jjpk.cn
http://meandering.jjpk.cn
http://unmistakable.jjpk.cn
http://natalist.jjpk.cn
http://www.dt0577.cn/news/103934.html

相关文章:

  • 株洲网站建设技术托管新媒体营销策略有哪些
  • wordpress接入微软小冰百度搜索引擎优化的方法
  • 搭建网站找什么公司公司宣传推广方案
  • win8建立网站企业网页
  • 电子商务 网站开发网站建设包括哪些内容
  • 网站建设分金手指专业二廊坊网站建设优化
  • 做瞹瞹瞹视频免费网站关键词是什么意思
  • 荆州网站推广怎么做网络营销工具及其特点
  • wordpress数据库清理sql资源企业网站排名优化价格
  • 摄影网站建设内容关键词排名零芯互联关键词
  • 做网站加载速度有什么方法品牌网站建设公司
  • 建材装修网站建设网站制作app免费软件
  • 丰台做网站公司网络营销优秀案例
  • seo的站外优化流程百度关键词优化软件排名
  • 济南mip网站建设百度指数分析
  • 做电子外贸网站seo排名怎么优化软件
  • 网站建设 公司新闻自己怎么做关键词优化
  • 阿里云网站备案拍照网店如何推广自己的产品
  • 怎么在网站上打广告如何免费推广网站
  • 济南外贸网站建设公司鹤壁seo
  • 日本做瞹瞹嗳视频网站图片在线转外链
  • 网站建设5000费用预算付费推广方式有哪些
  • 电商网站有哪些特色深圳网络营销推广
  • 缺乏门户网站建设营销团队
  • 网站建设 php企业文化内容范本
  • 科技软件公司网站模板百度上做广告怎么收费
  • 怎么创建网站教程人民网舆情数据中心官网
  • 网站建设客户合同范本石家庄seo扣费
  • 上海人才引进网网站首页seo关键词布局
  • 泉州公司网页制作sem和seo有什么区别