对于阴影的绘制,首先需要使用的是渐变色的绘制,在Android中,可以使用GradientDrawable对象中的setBounds和draw来进行绘制。

参考文档如下:https://developer.android.google.cn/reference/android/graphics/drawable/Drawable

  • The setBounds(Rect) method must be called to tell the Drawable where it is drawn and how large it should be. All Drawables should respect the requested size, often simply by scaling their imagery. A client can find the preferred size for some Drawables with the getIntrinsicHeight() and getIntrinsicWidth() methods.

必须调用setBounds(Rect)方法来告诉Drawable在何处绘制以及它应该有多大。

其中,setBounds还有一个重载方法:

setBounds

Added in API level 1

public void setBounds (int left, int top, int right, int bottom)

也就是可以指定这个渐变区域的位置,即(left, top) -> (right, bottom) 的矩形区域。

对于这个区域,然后我们可以设置一个两个颜色的渐变,然后就可以做出阴影效果。

对于画出阴影部分,我们这里还是新建一个Canvas来进行绘制,如:

Bitmap bitmap1 = Bitmap.createBitmap((int) viewWidth, (int) viewHeight, Bitmap.Config.ARGB_8888);
Canvas bitmapCanvastemp = new Canvas(bitmap2);int deepColor = 0xFF000000;  // 黑色
int lightColor = 0x0F333333; // 灰色
int[] gradientColors = new int[]{deepColor, lightColor};//渐变颜色数组
GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);int left = 0;
int right = 50;
int top = 0;
int bottom = viewHeight;
gradientDrawable.setBounds(left, top, right, bottom);gradientDrawable.draw(bitmapCanvastemp);

阴影部分效果,如下图:

那么,对于从底部开始的翻页,我们只需要将这个阴影进行动态指定其绘制的宽度以及旋转角度即可绘制出来。

这里,不妨在原草图上进行阴影部分的绘制:

我们建立如上参考线,我们的目标是:

  • 知道黑色部分的宽度;
  • 知道从b点旋转的角度θ\thetaθ;

在上面草图中,记黑色部分宽度为L,那么根据r为其四分位点,可以很容易从a和h点的坐标求得其长度:
(4L)2=(ax−hx)2+(ay−hy)2L=(ax−hx)2+(ay−hy)2/4(4L)^2 = (a_x - h_x)^2 + (a_y - h_y)^2 \\ L = \sqrt{(a_x - h_x)^2 + (a_y - h_y)^2} / 4 (4L)2=(ax​−hx​)2+(ay​−hy​)2L=(ax​−hx​)2+(ay​−hy​)2​/4
那么,我们需要求旋转角度,由于我们可以计算出b和f的坐标,故而可以求得其正弦值:
tan⁡(θ)=abs(fx−bx)abs(fy−by)\tan(\theta) = \frac{abs(f_x - b_x)}{abs(f_y - b_y)} tan(θ)=abs(fy​−by​)abs(fx​−bx​)​
那么可以求得:
θ=arctan⁡(abs(fx−bx)abs(fy−by))\theta = \arctan(\frac{abs(f_x - b_x)}{abs(f_y - b_y)}) θ=arctan(abs(fy​−by​)abs(fx​−bx​)​)
对应的Java代码为:

float L = (float) (Math.sqrt(Math.pow((h.x - points.get("a").x), 2f) + Math.pow((h.y - points.get("a").y), 2f))) / 4;float degree = (float) Math.toDegrees(Math.atan(Math.abs((points.get("b").x-points.get("f").x) / (points.get("b").y-points.get("f").y))));

那么,可以使用:

gradientDrawable.rotate(degree, points.get("b").x, points.get("b").y);

进行阴影部分的旋转。

绘制阴影部分的代码如下:

Canvas bitmapCanvastemp = new Canvas(bitmap2);int deepColor = 0xFF000000;  // 黑色
int lightColor = 0x0F333333; // 灰色
int[] gradientColors = new int[]{deepColor, lightColor};//渐变颜色数组
GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);float L = (float) (Math.sqrt(Math.pow((h.x - points.get("a").x), 2f) + Math.pow((h.y - points.get("a").y), 2f))) / 4;
int left = (int) points.get("b").x;
int right = (int) (points.get("b").x + L);
int top = (int) 0 - viewWidth;
int bottom = (int) h.y;
gradientDrawable.setBounds(left,top, right,bottom);float degree = (float) Math.toDegrees(Math.atan(Math.abs((points.get("b").x-points.get("f").x) / (points.get("b").y-points.get("f").y))));
Log.e("mDegrees", String.valueOf(degree) );
bitmapCanvastemp.rotate(degree, points.get("b").x, points.get("b").y);gradientDrawable.draw(bitmapCanvastemp);

不妨将这个功能封装到一个工具类中:

public class DrawShadowCustom {/**** @param bitmap 相当于缓存* @param points 所有的点 a, b, c, d, e, f, g, i, j, h* @param viewWidth 屏幕宽度* @param viewHeight 屏幕高度* @param gradientColors 渐变颜色数组*/public static void drawBottomRightShadow(Bitmap bitmap, Map<String, MyPoint> points, float viewWidth, float viewHeight, @Nullable int[] gradientColors){if(gradientColors == null){int deepColor = 0xFF000000;  // 黑色int lightColor = 0x0F333333; // 灰色gradientColors = new int[]{deepColor, lightColor};//渐变颜色数组}Canvas bitmapCanvastemp = new Canvas(bitmap);GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);float L = (float) (Math.sqrt(Math.pow((points.get("h").x - points.get("a").x), 2f) + Math.pow((points.get("h").y - points.get("a").y), 2f))) / 4;int left = (int) points.get("b").x;int right = (int) (points.get("b").x + L);int top = (int) (0 - viewWidth); // 让这个矩形更长点,不会有不全的情况int bottom = (int) viewHeight;gradientDrawable.setBounds(left,top, right, bottom);float degree = (float) Math.toDegrees(Math.atan(Math.abs((points.get("b").x-points.get("f").x) / (points.get("b").y-points.get("f").y))));Log.e("rDegree", String.valueOf(degree));bitmapCanvastemp.rotate(degree, points.get("b").x, points.get("b").y);gradientDrawable.draw(bitmapCanvastemp);}}

效果如下图所示:


接着,我们需要对底部左边的阴影进行绘制:

这里iQ平行于ac,jQ平行于ag,相交于Q点。

那么,我们可以绘制出图中白色矩形,然后以Q为控制点,旋转θ\thetaθ度即可。我们当前的任务为三个:

  • 求矩形的宽度;
  • 求旋转的角度;
  • 求Q的坐标;

对于宽度,为了方便计算,不妨先假设im为图中小三角形的垂直平分线。

那么,由
cos(θ)=Llic=L(ix−cx)2+(iy−cy)2cos(\theta) = \frac{L}{l_{ic}} \\ = \frac{L} {\sqrt{(i_x - c_x)^2 + (i_y - c_y)^2}} cos(θ)=lic​L​=(ix​−cx​)2+(iy​−cy​)2​L​
也就是:
θ=arccos⁡(L(ix−cx)2+(iy−cy)2)\theta = \arccos(\frac{L} {\sqrt{(i_x - c_x)^2 + (i_y - c_y)^2}}) θ=arccos((ix​−cx​)2+(iy​−cy​)2​L​)
由于im垂直于直线ac,那么点到直线的距离可以计算出
L=∣iy−k1∗ix−b1∣k12+1k1=cy−aycx−ax;b1=ay−k1∗axL = \frac{|i_y - k_1*i_x - b_1|}{\sqrt{k_1^2 + 1}} \\ k_1 = \frac{c_y - a_y} {c_x - a_x}; \\ b_1 = a_y - k_1 * a_x L=k12​+1​∣iy​−k1​∗ix​−b1​∣​k1​=cx​−ax​cy​−ay​​;b1​=ay​−k1​∗ax​
对于Q点坐标,由于其在直线iQ上,我们可以求得直线方程Y5:
Y5=k1∗X5+b5b5=iy−k1∗ixY_5 = k_1 * X_5 + b_5 \\ b_5 = i_y - k_1 * i_x \\ Y5​=k1​∗X5​+b5​b5​=iy​−k1​∗ix​
即:
Y5=k1∗X5+(iy−k1∗ix)Y_5 = k_1 * X_5 + (i_y - k_1 * i_x) Y5​=k1​∗X5​+(iy​−k1​∗ix​)
不妨设Q坐标为(q_x, q_y),那么:
qy=k1∗qx+(iy−k1∗ix)q_y = k_1 * q_x + (i_y - k_1 * i_x) qy​=k1​∗qx​+(iy​−k1​∗ix​)
由角度关系:
cos⁡(θ)=iy−qy(qx−ix)2+(qy−iy)2\cos(\theta) = \frac{i_y - q_y}{\sqrt{(q_x - i_x)^2 + (q_y - i_y)^2}} cos(θ)=(qx​−ix​)2+(qy​−iy​)2​iy​−qy​​
也就是解二元一次方程组问题:

但是,求cos这里比较复杂, 因为Q在直线Qj上,设该直线为Y_6,同理:

Y6=k2∗X6+b6b5=gy−k2∗gxY6=k2∗X6+(gy−k2∗gx)Y_6 = k_2 * X_6 + b_6 \\ b_5 = g_y - k_2 * g_x \\ Y_6 = k_2 * X_6 + (g_y - k_2 * g_x) \\ Y6​=k2​∗X6​+b6​b5​=gy​−k2​∗gx​Y6​=k2​∗X6​+(gy​−k2​∗gx​)

也就是联立下面两个方程组问题:
Y5=k1∗X5+(iy−k1∗ix)Y6=k2∗X6+(gy−k2∗gx)Y_5 = k_1 * X_5 + (i_y - k_1 * i_x) \\ Y_6 = k_2 * X_6 + (g_y - k_2 * g_x) \\ Y5​=k1​∗X5​+(iy​−k1​∗ix​)Y6​=k2​∗X6​+(gy​−k2​∗gx​)
带入Q(q_x, q_y),有:
qx=(iy−k1∗ix)−(gy−k2∗gx)k2−k1qy=k2∗qx+(gy−k2∗gx)q_x = \frac{(i_y - k_1 * i_x) - (g_y - k_2 * g_x)}{k_2 - k_1} \\ q_y = k_2 * q_x + (g_y - k_2 * g_x) qx​=k2​−k1​(iy​−k1​∗ix​)−(gy​−k2​∗gx​)​qy​=k2​∗qx​+(gy​−k2​∗gx​)

这里还是将角度关系转为tan函数:
θ=arctan⁡(qx−ixiy−qy)\theta = \arctan(\frac{q_x - i_x}{i_y - q_y}) θ=arctan(iy​−qy​qx​−ix​​)
值得注意的是这里不能用abs,因为此时的角度可能大于90度。代码如下:

float k_1 = (points.get("c").y - points.get("a").y) / (points.get("c").x - points.get("a").x);
float b_1 = points.get("a").y - k_1 * points.get("a").x;
float L = (float) (Math.abs(points.get("i").y - k_1 * points.get("i").x - b_1) / Math.sqrt(k_1 * k_1 + 1));float k_2 = (points.get("g").y - points.get("a").y) / (points.get("g").x - points.get("a").x);float q_x = (points.get("i").y - k_1 * points.get("i").x - points.get("g").y + k_2 * points.get("g").x) / (k_2 - k_1);
float q_y = k_2 * q_x + points.get("g").y - k_2 * points.get("g").x;Canvas bitmapCanvastemp = new Canvas(bitmap1);int deepColor = 0xFF666666;  // 黑色
int lightColor = 0x08333333; // 灰色
int[] gradientColors = new int[]{lightColor, deepColor};//渐变颜色数组
GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);int left = (int) q_x;
int right = (int) (q_x + 3 * L / 2);
int top = (int) q_y;
int bottom = (int) points.get("i").y * 2;
gradientDrawable.setBounds(left,top, right,bottom);float degree = (float) Math.toDegrees(Math.atan((q_x-points.get("i").x) / (points.get("i").y - q_y)));
Log.e("mDegrees", String.valueOf(degree) );
bitmapCanvastemp.rotate(degree, q_x, q_y);gradientDrawable.draw(bitmapCanvastemp);

同样封装在上面的工具类中:

 public static void drawBottomLeftShadow(Bitmap bitmap, Map<String, MyPoint> points, @Nullable int[] gradientColors){float k_1 = (points.get("c").y - points.get("a").y) / (points.get("c").x - points.get("a").x);float b_1 = points.get("a").y - k_1 * points.get("a").x;float L = (float) (Math.abs(points.get("i").y - k_1 * points.get("i").x - b_1) / Math.sqrt(k_1 * k_1 + 1));float k_2 = (points.get("g").y - points.get("a").y) / (points.get("g").x - points.get("a").x);float q_x = (points.get("i").y - k_1 * points.get("i").x - points.get("g").y + k_2 * points.get("g").x) / (k_2 - k_1);float q_y = k_2 * q_x + points.get("g").y - k_2 * points.get("g").x;Canvas bitmapCanvastemp = new Canvas(bitmap);if(gradientColors == null){int deepColor = 0xFF666666;  // 黑色int lightColor = 0x08333333; // 灰色gradientColors = new int[]{lightColor, deepColor};//渐变颜色数组}GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);int left = (int) q_x;int right = (int) (q_x + 3 * L / 2);int top = (int) q_y;int bottom = (int) points.get("i").y * 2;gradientDrawable.setBounds(left,top, right,bottom);float degree = (float) Math.toDegrees(Math.atan((q_x-points.get("i").x) / (points.get("i").y - q_y)));Log.e("mDegrees", String.valueOf(degree) );bitmapCanvastemp.rotate(degree, q_x, q_y);gradientDrawable.draw(bitmapCanvastemp);}

效果:


接下来,我们将底部的顶部阴影来补全。同样的,来绘制一些辅助参考线,由于Q点坐标已经确定个,这里只需要计算旋转的角度即可,如下图:


可以看出此时的角度为:
−(90−上次的角度θ)-(90 - 上次的角度\theta) −(90−上次的角度θ)
那么,不妨将之放到一起。即:

public static void drawBottomLeftShadow(Bitmap bitmap, Map<String, MyPoint> points, @Nullable int[] gradientColors){float k_1 = (points.get("c").y - points.get("a").y) / (points.get("c").x - points.get("a").x);float b_1 = points.get("a").y - k_1 * points.get("a").x;float L = (float) (Math.abs(points.get("i").y - k_1 * points.get("i").x - b_1) / Math.sqrt(k_1 * k_1 + 1));float k_2 = (points.get("g").y - points.get("a").y) / (points.get("g").x - points.get("a").x);float q_x = (points.get("i").y - k_1 * points.get("i").x - points.get("g").y + k_2 * points.get("g").x) / (k_2 - k_1);float q_y = k_2 * q_x + points.get("g").y - k_2 * points.get("g").x;Canvas bitmapCanvastemp = new Canvas(bitmap);if(gradientColors == null){int deepColor = 0xFF666666;  // 黑色int lightColor = 0x08333333; // 灰色gradientColors = new int[]{lightColor, deepColor};//渐变颜色数组}GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);int left = (int) q_x;int right = (int) (q_x + 3 * L / 2);int top = (int) q_y;int bottom = (int) points.get("i").y * 2;gradientDrawable.setBounds(left,top, right,bottom);float degree = (float) Math.toDegrees(Math.atan((q_x-points.get("i").x) / (points.get("i").y - q_y)));Log.e("mDegrees", String.valueOf(degree) );bitmapCanvastemp.rotate(degree, q_x, q_y);gradientDrawable.draw(bitmapCanvastemp);left = (int) q_x;right = (int) (q_x + L);top = (int) q_y;bottom = (int) points.get("i").y * 2;Canvas bitmapCanvastemp2 = new Canvas(bitmap);GradientDrawable gradientDrawable2 = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, new int[]{0xFFD7C7A9, 0xFF8F897E});gradientDrawable2.setBounds(left,top, right,bottom);degree = 90 - (float) Math.toDegrees(Math.atan((q_x-points.get("i").x) / (points.get("i").y - q_y)));Log.e("mDegrees", String.valueOf(degree) );bitmapCanvastemp2.rotate(-1 * degree, q_x, q_y);gradientDrawable2.draw(bitmapCanvastemp2);
}

效果:
但是分界线比较明显,比较难看!

观察上图,发现底部左边和上边的阴影部分貌似可以直接使用一个旋转的矩形解决!

猛然发现,貌似Q点坐标求错了。

qx=(iy−k1∗ix)−(jy−k2∗jx)k2−k1qy=k2∗qx+(jy−k2∗jx)q_x = \frac{(i_y - k_1 * i_x) - (j_y - k_2 * j_x)}{k_2 - k_1} \\q_y = k_2 * q_x + (j_y - k_2 * j_x) qx​=k2​−k1​(iy​−k1​∗ix​)−(jy​−k2​∗jx​)​qy​=k2​∗qx​+(jy​−k2​∗jx​)
那么,我们使用矩形就够了。但是实际上却是不好看。如下图:

所以看来还是用哪个无意间计算错了的好些。

所以,这里就不再使用渐变,直接使用纯色的矩形:

public static void drawBottomTopShadow(Context context, Bitmap bitmap, Map<String, MyPoint> points, @Nullable int[] gradientColors){float k_1 = (points.get("c").y - points.get("a").y) / (points.get("c").x - points.get("a").x);float b_1 = points.get("a").y - k_1 * points.get("a").x;float L = (float) (Math.abs(points.get("i").y - k_1 * points.get("i").x - b_1) / Math.sqrt(k_1 * k_1 + 1));float k_2 = (points.get("g").y - points.get("a").y) / (points.get("g").x - points.get("a").x);float q_x = (points.get("i").y - k_1 * points.get("i").x - points.get("g").y + k_2 * points.get("g").x) / (k_2 - k_1);float q_y = k_2 * q_x + points.get("g").y - k_2 * points.get("g").x;Canvas bitmapCanvastemp = new Canvas(bitmap);int left = (int) q_x;int right = (int) (q_x + 1.5 * L);int top = (int) q_y;int bottom = (int) points.get("i").y * 2;Paint paint = new Paint();paint.setColor(context.getResources().getColor(R.color.shadowColor)); // D0C1A5float degree = (float) Math.toDegrees(Math.atan((q_x-points.get("i").x) / (points.get("i").y - q_y)));bitmapCanvastemp.rotate(degree, q_x, q_y);bitmapCanvastemp.drawRect(left, top, right, bottom, paint);left = (int) q_x;right = (int) (q_x + L);top = (int) q_y;bottom = (int) points.get("i").y * 2;Canvas bitmapCanvastemp2 = new Canvas(bitmap);degree = 90 - (float) Math.toDegrees(Math.atan((q_x-points.get("i").x) / (points.get("i").y - q_y)));Log.e("mDegrees", String.valueOf(degree) );bitmapCanvastemp2.rotate(-1 * degree, q_x, q_y);bitmapCanvastemp2.drawRect(left, top, right, bottom, paint);
}

效果:

虽然这样确实不大对,但是却看着正常了很多。

Android翻页入门学习(三)阴影绘制相关推荐

  1. Android翻页入门

    1. 前言 欲整理和实现Android端的翻页效果实现,并想将之整理打包成为一个成熟的第三方插件.不知道会用多少时间来实现这个功能,虽然网上已经有现成的项目,以及对之的解析,但本人从学习的角度来说,不 ...

  2. OpenGL入门学习[三]

    OpenGL入门学习[三] http://xiaxveliang.blog.163.com/blog/static/2970803420126246501930/ OpenGL入门学习[十一] 我们在 ...

  3. AD入门学习—原理图的绘制3

    目录 2.4 CAN&24C02及DS18B20温度传感单元的绘制 2.5 USB单元的绘制 2.6 SD卡及TFT单元的绘制 2.7 NRF24L01单元的绘制 2.8 COM口及PS/2接 ...

  4. PCB入门学习—原理图的绘制2

    目录 2.2 TEA5767音频模块的绘制 2.3 ENC28J60以太网模块的绘制 学习目录: 2.2 TEA5767音频模块的绘制 首先有个问题:为什么这个电容放在原理图上怎么移动也对不齐?? 解 ...

  5. Redis6入门学习(三)--Redis_Jedis、事务、LUA脚本

    title: Redis6入门学习(三) 文章目录 title: Redis6入门学习(三) **Redis_Jedis_测试** **Jedis所需要的jar包** **连接Redis注意事项** ...

  6. Android翻页效果原理实现之翻页的尝试

    炮兵镇楼 在<自定义控件其实很简单>系列的前半部分中我们用了整整六节近两万字两百多张配图讲了Android图形的绘制,虽然篇幅很巨大但仍然只是图形绘制的冰山一角,旨在领大家入门,至于修行成 ...

  7. android翻页实现原理

    Android平台中的二种翻页效果实现. 第一种翻页效果如下: 实现原理: 当前手指触摸点为a,则 a点坐标为(ax,ay), 由三角形acb与三角形cmb为对称三角形并且直线cp为am垂直平分线,则 ...

  8. Android 翻页效果 电子书 (转)

    转载请注明来自: 5进制空间-android区 相信做电子书的同学,都遇到过翻页动画的需求吧,如果你不满足与点击滑动翻页的话,这边文章应该能够帮助到你. 先上个效果图: [img]http://www ...

  9. Android翻页效果原理实现之引入折线

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 尊重原创 ...

最新文章

  1. UITableVeiw相关的需求解决
  2. 广东移动携手远传技术 共建移动客服标杆
  3. OpenCV3编程入门(毛星云)之用滚动条控制两图片的混合
  4. python 类继承与子类实例初始化
  5. leetcode 242. 有效的字母异位词(Java版)
  6. php截取字符串函数 左右,php截取中文字符串函数的技巧
  7. Transformer太深不行?NUS字节发现注意力坍缩,提出重注意机制!
  8. 在Android中显示GIF动画
  9. SAP 产品条码WMS结合 以及ABAP script的集成 BarCode
  10. HDU1847 博弈论 水题
  11. PHP中cURL的curl_getinfo函数返回的CURLINFO_HTTP_CODE是0
  12. php多条件查询统计,PHP-----多条件查询
  13. 数分项目《泰坦尼克》——Task1
  14. 青铜器RDM与PTC Windchill协同构建货真价实的PLM解决方案
  15. 4.2 分类效果的评价
  16. Java基础--数值和字符串
  17. 【BZOJ4379】[POI2015]Modernizacja autostrady 树形DP
  18. html转换为pdf php,js实现html转成pdf
  19. c语言编活期储蓄银行系统,C语言编程1活期存款。活期利息每一季度结算...
  20. 908c语言程序设计,2021考研大纲:武汉轻工大学908C语言程序设计2021年硕士研究生入学考试初试科目考试大纲...

热门文章

  1. angular学习-2021.5
  2. mac配置node的环境变量,-bash: ls:command not fund
  3. 学习记录:python列表字典嵌套
  4. 【超清】Autodesk Maya2013 安装过程
  5. 安装 Microsoft.VisualStudio.Devenv.Msi 报错
  6. 【个性化推荐系统】简介
  7. 基于Visual C++2010 与office2010开发办公自动化(23)- 使用剪贴板导出Excel文件
  8. 谈谈网页中使用奇数字体和偶数字体
  9. L2-1 功夫传人(25 分)
  10. 需求工程小黑指北-简答速记