[Android进阶]OFO首页实现小窥

[Android进阶]OFO首页实现小窥

个人微信公众号,欢迎大家加入。
这里写图片描述
最近阅读量凄凄惨惨,难以为继,孤倍感无力,遂决定着眼于炫酷,造一些博眼球的东西以引流,比如说实现XXX页面效果,仿XXX页面效果等,各位看官如若觉得不错,还请动动手指点点赞,能转发一波就更好了,嘿呀,不说废话了,开撸。

做为这种博眼球系列的开篇,第一篇就从去年的共享经济着手仿制了,今天要实现的是OFO的首页页面效果,下面我们一起观察下OFO首页的页面构图:
这里写图片描述

从上图我们可以看出,OFO首页整体上是一个帧布局,页面底部的操作栏遮罩在最底下的MapView上,相信大家都能写出来,唯一有疑惑的地方可能是底部的实现细节,也就是下图部分:
这里写图片描述

不考虑实现细则,结合第一张图的布局边界,我们可以做如下猜测,该部分由如下五个构建组成:
1.上弧矩形
2.向下的箭头
3.圆形的扫码用车
4.用户头像
5.铃铛
那么这五个构建该怎么用Android平台的技术实现呢?
不妨将实现方案列成如下的对应表(PS:表中的太Low,大家自己玩):

界面部分实现方式1
向下箭头ImageView/ImageButton
用户头像ImageView/ImageButton
铃铛ImageView/ImageButton

那么上弧矩形和圆形的扫码用车怎么整呢?好像Android控件中并没有这样的东西,该怎么实现呢?


扫码用车的实现

Shape是Android中一种XML内的图形绘制方式,可以使用Shape定义圆角矩形,矩形,圆形,椭圆等形状。Shape文件的基本格式如下:

<?xml version="1.0" encoding="utf-8"?>
<shape
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape=""
  android:useLevel="false">
  <solid android:color=""/>
  <stroke android:width=""/>
  <size
    android:height=""
    android:width=""/>
</shape>

其中<shape></shape>根说明该文件是一个Shape资源文件,android:shape用于指定当前的形状类型,可参照下表:

图形android:shape
矩形rectangle
椭圆oval
线line
ring

<size></size>用于指定Shape的宽高,<solid></solid>用于指定shape的填充颜色,<stroke></stroke>用于指定描边的相关信息,另外还有<corners></corners>(指定圆角信息),<gradient></gradient>(指定渐变信息)及<padding></padding>(指定内部padding信息)。

扫码骑车的背景可定义为如下shape:

<?xml version="1.0" encoding="utf-8"?>
<shape
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="oval"
  android:useLevel="false">
  <solid android:color="@color/colorWhite"/>
  <size
    android:height="20dp"
    android:width="20dp"/>
</shape>

运行后效果如下:
这里写图片描述
更多shape细节大家可以自行尝试。

前三种实现都是将Shape做为整体的背景,思路很清晰,这里只贴出代码,不做赘述。

Shape+LinearLayout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="100dp"
  android:layout_height="100dp"
  android:background="@drawable/scan_image_bg"
  android:layout_margin="20dp"
  android:orientation="vertical">
  <ImageView
    android:layout_gravity="center_horizontal"
    android:layout_marginTop="20dp"
    android:src="@drawable/qr_scnner"
    android:layout_width="40dp"
    android:layout_height="40dp"/>
  <TextView
    android:layout_gravity="center_horizontal"
    android:gravity="center"
    android:text="扫码用车"
    android:layout_width="wrap_content"
    android:layout_height="40dp"/>
</LinearLayout>

效果如下:
这里写图片描述

Shape+TextView

使用TextView的drawableTop属性,避免编写LinearLayout,降低渲染成本,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
  <TextView
    android:background="@drawable/scan_image_bg"
    android:layout_centerInParent="true"
    android:gravity="center_horizontal"
    android:textSize="20sp"
    android:paddingTop="50dp"
    android:drawableTop="@drawable/qr_scnner"
    android:text="扫码骑车"
    android:layout_width="200dp"
    android:layout_height="200dp"/>
</RelativeLayout>

效果如下图:
这里写图片描述

上弧矩形的实现

图片

对于上弧矩形背景,我们肯定是能用.9图片解决问题的,这里不再赘述,关于在Android Studio内制作.9图片的方式,有需要的小伙伴可以留言,我下期推送。

自定义Drawable

除了使用.9图片我们也可以使用自定义Drawable来实现,仔细观察,我们可以发现上弧矩形和普通矩形最大的区别在于,有一边是弧形,而弧形我们可以通过贝塞尔曲线实现,这里我们只需要绘制一个填充颜色的path即可,如下图:
这里写图片描述
从上图我们可以看出,绘制一个上弧矩形,关键路径有四条,A’B,BC,CD’,A’D’,对于A’D’而言,我们可以通过AD平分线上的控制点E决定它的弯曲程度,A’,D’两点坐标可以依赖于AD两点获取,那么所有问题便都迎刃而解了,代码如下:

public class ArcBackgroundDrawable extends Drawable {

    private Path mPath;

    @Override
    public void draw(@NonNull Canvas canvas) {
        mPath = new Path();
        //获取Drawable的边界
        Rect bounds = getBounds();
        //移动Path起点到A'处,其中AA'占AB总长度的十分之一

        mPath.moveTo(bounds.left, bounds.top+(bounds.bottom-bounds.top)/10);
        //绘制A'D',控制点在AD平分线上
        mPath.quadTo(bounds.left+(bounds.right-bounds.left)/2 , bounds.top-(bounds.bottom-bounds.top)/10, bounds.right , bounds.top+(bounds.bottom-bounds.top)/10);

        //绘制D'C
        mPath.lineTo(bounds.right, bounds.bottom);
        //绘制CB
        mPath.lineTo(bounds.left, bounds.bottom);

        //闭合曲线,自动绘制BA'
        mPath.close();
        //填充Path内为黄色
        canvas.clipPath(mPath);
        canvas.drawColor(Color.YELLOW);
    }

    @Override
    public void setAlpha(int alpha) {

    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {

    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }
}

如上述步骤,我们便完成了一个上弧矩形的绘制,效果如下:
这里写图片描述

自定义View

自定义View与自定义Drawable思路一致,这里不再赘述,贴出代码即可。

public class ArcBackgroundView extends View {
    private float mViewWidth;
    private float mViewHeight;
    private int mBackgroundColor;
    private Paint mArcPaint;
    private Path mPath;

    public ArcBackgroundView(Context context) {
        super(context);
    }

    public ArcBackgroundView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public ArcBackgroundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public ArcBackgroundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mViewWidth = w;
        mViewHeight = h;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        calculatePath();
        canvas.clipPath(mPath);
        canvas.drawColor(mBackgroundColor);
    }

    private void init(Context context) {
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(R.styleable.ArcBackgroundView);
        mBackgroundColor = typedArray.getColor(R.styleable.ArcBackgroundView_backgroundColor, Color.YELLOW);
        mPath = new Path();
        mArcPaint = new Paint();
        mArcPaint.setColor(mBackgroundColor);
    }

    private void calculatePath() {

        mPath.moveTo(getLeft(), getTop()+(getBottom()-getTop())/10);
        mPath.quadTo(getLeft()+(getRight()-getLeft())/2 , getTop()-(getBottom()-getTop())/10, getRight() , getTop()+(getBottom()-getTop())/10);

        mPath.lineTo(getRight(), getBottom());
        mPath.lineTo(getLeft(), getBottom());

        mPath.close();
    }
}

如此则解决了所有组件的绘制问题,接下来就是使用合适的layout将这五个组件组合起来了,组合后我们就实现了OFO的首页效果,运行结果如下:
这里写图片描述
其实我们还可以使用自定义ViewGroup,乃至于自定义View直接绘制这个底部操作栏,这里只是为大家提供一种辩证思路,希望对大家有所启发,Thanks for ur reading.完整代码参见:

https://github.com/tuozhaobing/CsdnDemoCode/tree/master/OFOPageDemo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值