美团点评android面试题,【大众点评知乎美团面试题】面试问题:Android… - 看准网...

效果图

包含的技术点

这个知乎的详情页面所包含的几个技术点:

1. support.v7包下的ToolBar的使用

2. ScrollView实现滑动顶部停靠

3. 监听手势滑动方向来显示和隐藏底部视图

ToolBar的使用

知乎的Material Design版本顶部的导航是一个ToolBar控件,ToolBar是support.v7包下的一个控件,ToolBar的使用非常简单,首先我们现在layout文件夹中新建一个ToolBar.xml

xmlns:local="http://schemas.android.com/apk/res-auto"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:id="@+id/toolbar"

android:background="?attr/colorPrimary"

local:theme="@style/Base.ThemeOverlay.AppCompat.Dark.ActionBar"

android:popupTheme="@style/ThemeOverlay.AppCompat.Light"

>

此时我们的界面预览应该是这样的

a8901a4eb8704d606392285e369a1a39.png

接下来我们在MainActivity中进行一下设置

public class MainActivity extends AppCompatActivity{

private Toolbar mToolbar;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mToolbar = (Toolbar) findViewById(R.id.toolbar);

mToolbar.setTitle("历史上有哪些打脸的故事?");

setSupportActionBar(mToolbar);

}

接下来我们添加ToolBar中的分享和菜单按钮功能,他们是menu

8d6d3530b54c6a250df82f9990c0a4be.png

我们在我们的工程目录下的res文件夹上面单击右键,点击New,然后点击Image Asset

ab3746aaa95fd1a1fc83ef48d18d50bb.png

在出来的菜单中的Asset Type中选择Action Bar and Tab Icons

839b961ca53ac9b6b53a066aedc4cc48.png

然后在Image File中选择图片的路径,在Resource name中填写图片的名称

7c8d7718e2c2920223c5e5c94361a0be.png

然后点击下一步就可以了,然后Android Studio就会自动帮我们创建下面这几个文件夹,并且图片也被添加进去了

bde7cc489d4cd3846b7bdb419b6853ae.png

接下来我们在res下创建一个menu文件夹,在menu文件夹内创建一个menu_main.xml

xmlns:app="http://schemas.android.com/apk/res-auto"

>

android:title="分享"

android:icon="@drawable/ic_action_share"

app:showAsAction="always"

/>

android:title="菜单"

android:icon="@drawable/ic_action_menu"

app:showAsAction="always"

/>

接下来在MainActivity中重写onCreateOptionsMenu和onOptionsItemSelected方法,这段代码非常简单,而且我们新建项目的时候可以自动帮我们生成,我们只需要稍微修改下就可以了,我就不一一解释了

@Override

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.menu_main, menu);

return true;

}

@Override

public boolean onOptionsItemSelected(MenuItem item) {

int id = item.getItemId();

if (id == R.id.action_share){

Toast.makeText(MainActivity.this,"分享",Toast.LENGTH_SHORT).show();

return true;

}

if (id == R.id.action_menu){

Toast.makeText(MainActivity.this,"菜单",Toast.LENGTH_SHORT).show();

return true;

}

return super.onOptionsItemSelected(item);

}

好了,ToolBar已经准备完毕了,我们可以运行一下项目看一下效果,是不是和知乎的一样!

ScrollView实现滑动顶部停靠

这个功能我是参考Android 仿美团网,大众点评购买框悬浮效果之修改版这篇文章来实现的,他的实现思路非常牛x,我最开始的想法是监听要停留在顶部的View的滑动位置,当它滑动到顶部的时候再创建出一个和他一模一样的View显示在顶部的位置,但是我发现这样做非常的麻烦,但是这篇文章的作者使用了另一种思路,他将两个View都创建出来,只不过他们一开始是重合的,我们看上去就像只有一个View一样,当View滑动到顶部时,上面覆盖的那个View就固定住了,从而我们视觉上感觉是这个View停靠在了顶部。

d92b7cd659511a17e99e8f45005bdd65.png

我们创建两个一模一样的布局,一个是蓝色覆盖在上面,一个是红色在下面,当我们向上滑动的时候,红色的顺着滑出去,而上面的蓝色的就停留在顶部了,这样就形成了滑动顶部停靠的效果。

首先我们先自定义一个ScrollView,由于ScrollView没有onScrollListener,所以我们必须要自己写一个onScrollListener

public class MyScrollView extends ScrollView{

private OnScrollListener mListener;

public interface OnScrollListener{

void onScroll(int scrollY);

}

public MyScrollView(Context context) {

super(context);

}

public MyScrollView(Context context, AttributeSet attrs) {

super(context, attrs);

}

public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

}

public void setOnScrollListener(OnScrollListener listener){

mListener = listener;

}

@Override

protected int computeVerticalScrollRange() {

return super.computeVerticalScrollRange();

}

@Override

protected void onScrollChanged(int l, int t, int oldl, int oldt) {

super.onScrollChanged(l, t, oldl, oldt);

if (mListener!=null){

mListener.onScroll(t);

}

}

}

我们在onScrollChanged中可以获得当前ScrollView的滑动位置,我们回调调用mListener的onScroll方法并且将当前ScrollView的滑动位置传给MainActivity

在MainActivity中我们实现MyScrollView.OnScrollListener接口,并且重写onScroll方法,在onScroll方法中设置蓝色View的位置为和红色View重合

先来看一下activity_main.xml

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

android:id="@+id/container"

tools:context="com.zhangqi.zhihudetail.MainActivity">

layout="@layout/toolbar" />

android:layout_below="@id/toolbar"

android:layout_width="match_parent"

android:layout_height="wrap_content"

>

android:layout_height="match_parent"

>

android:orientation="vertical"

android:layout_height="match_parent">

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:background="#efefef"

android:paddingBottom="10dp"

android:paddingLeft="5dp"

android:paddingTop="10dp"

android:text="历史上有哪些打脸的故事?"

android:textColor="#ababab"

android:textSize="18sp" />

layout="@layout/user_detail_view"

/>

android:layout_width="match_parent"

android:layout_height="wrap_content">

layout="@layout/user_detail_view"

/>

其中我将顶部停靠的View的布局抽取出来了,因为要重用,所以抽取出来使用include重用即可

android:id="@+id/rl_user_detail"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:background="#ffffffff"

android:padding="10dp">

android:id="@+id/iv_avatar"

android:layout_width="40dp"

android:layout_height="40dp"

android:src="@drawable/sso_zhihu_logo" />

android:id="@+id/tv_nickname"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_toRightOf="@id/iv_avatar"

android:paddingLeft="5dp"

android:text="神灯"

android:textColor="#ff000000"

android:textSize="16sp" />

android:id="@+id/tv_detail"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignLeft="@id/tv_nickname"

android:layout_below="@id/tv_nickname"

android:paddingLeft="5dp"

android:paddingTop="5dp"

android:text="阿拉灯神灯"

android:textColor="#bcbcbc" />

android:id="@+id/tv_like_num"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentRight="true"

android:layout_centerVertical="true"

android:drawableLeft="@drawable/ic_vote_normal"

android:drawablePadding="10dp"

android:gravity="center_vertical"

android:padding="5dp"

android:text="3028" />

android:layout_width="1dp"

android:layout_height="wrap_content"

android:layout_alignBottom="@id/tv_like_num"

android:layout_alignTop="@id/tv_like_num"

android:layout_centerVertical="true"

android:layout_marginBottom="2dp"

android:layout_marginRight="5dp"

android:layout_marginTop="2dp"

android:layout_toLeftOf="@id/tv_like_num"

android:background="#CCC" />

接下来看MainActivity

public class MainActivity extends AppCompatActivity implements MyScrollView.OnScrollListener{

//自定义的ScrollView

private MyScrollView mScrollView;

//随着ScrollView滑走的View

private RelativeLayout mUserDetail;

//固定在顶部的View

private RelativeLayout mTopUserDetail;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

initView();

}

private void initView() {

mUserDetail = (RelativeLayout) findViewById(R.id.user_detail);

mTopUserDetail = (RelativeLayout) findViewById(R.id.top_user_detail);

mScrollView = (MyScrollView) findViewById(R.id.myscrollview);

mScrollView.setOnScrollListener(this);

//当布局中所有的View都测量完后回回调的方法,我们在这个方法中可以拿到View的宽和高

//在这个方法中调用onScroll是为什么?

//因为我们要在onScroll中获得mUserDetail距顶部的高度

//只有在所有的View都测量完后我们才能拿到这个高度值,否则我们拿到的是0

//所以在onGlobalLayout中调用一下onScroll方法,我们一定可以拿到mUserDetail这个View

//距离屏幕顶部的距离,从而设置给我们的mTopUserDetail这个View,实现两个View的重合

findViewById(R.id.container).getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

@Override

public void onGlobalLayout() {

onScroll(mScrollView.getScrollY());

}

});

}

@Override

public void onScroll(int scrollY) {

//在最开始mUserDetail距离屏幕顶部是有一段距离的,而最开始scrollY=0,

//所以在最开始的时候我们取两者的最大值就可以使两个View重合起来

//因为我们是在所有的View都测量完毕后调用过onScroll方法的,

//所以mUserDetail.getTop()得到的值是正确的值

int userDetailView2Top = Math.max(scrollY, mUserDetail.getTop());

//调用mTopUserDetail的layout方法,设置其在屏幕上的位置

mTopUserDetail.layout(0, userDetailView2Top, mTopUserDetail.getWidth(), userDetailView2Top + mTopUserDetail.getHeight());

}

}

现在我们已经可以实现滑动停靠的功能了,接下来我们再来实现屏幕底部的View随着滑动方向显示和隐藏的功能

屏幕底部View随滑动方向显示和隐藏功能

我们看到屏幕底部有一个布局,当我们手指向上滑动的时候,底部的View是隐藏的,为了给我们更好地阅读体验,当我们手指向下滑动的时候,底部的View是显示出来的,提供给我们一些功能。

那么我们就要修改刚才自定义的ScrollView,给onScrollListener添加两个方法,一个是向上滑动,一个是向下滑动

public interface OnScrollListener{

void onScroll(int scrollY);

void onScrollToTop();

void onScrollToBottom();

}

那么我们怎么来判断用户是向上滑动还是向下滑动的呢?我们只需要重写ScrollView的onTouchEvent方法

@Override

public boolean onTouchEvent(MotionEvent ev) {

if (mListener!=null) {

switch (ev.getAction()) {

case MotionEvent.ACTION_DOWN:

//记录按下时的Y坐标

downY = (int) ev.getY();

break;

case MotionEvent.ACTION_MOVE:

//记录滑动时的Y坐标

int moveY = (int) ev.getY();

//计算出一个差值

offsetY = moveY - downY;

downY = moveY;

break;

case MotionEvent.ACTION_UP:

//当手指抬起时判断差值的大小

if (offsetY < 0) {//如果小于0,则说明用户手指向上滑动

mListener.onScrollToBottom();

}else{//如果大于0,则说明用户手指向下滑动

mListener.onScrollToTop();

}

break;

}

}

return super.onTouchEvent(ev);

}

接下来我们要在MainActivity中重写这两个方法

@Override

public void onScrollToTop() {

if (!ll_bottom.isShown()) {

ll_bottom.clearAnimation();

ll_bottom.startAnimation(showAnim);

ll_bottom.setVisibility(View.VISIBLE);

}

}

@Override

public void onScrollToBottom() {

if (ll_bottom.isShown()) {

ll_bottom.clearAnimation();

ll_bottom.startAnimation(dismissAnim);

ll_bottom.setVisibility(View.GONE);

}

}

其中ll_bottom就是我们底部的布局,他的xml如下

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:paddingTop="10dp"

android:paddingBottom="10dp"

android:layout_alignParentBottom="true"

android:background="#ffffffff"

android:orientation="horizontal">

android:layout_height="wrap_content"

android:layout_weight="1"

android:drawableTop="@drawable/ic_nohelp"

android:text="没有帮助"

android:textColor="#ababab"

android:drawablePadding="10dp"

android:gravity="center_horizontal"

/>

android:layout_height="wrap_content"

android:layout_weight="1"

android:drawableTop="@drawable/ic_thank"

android:text="感谢"

android:textColor="#ababab"

android:drawablePadding="10dp"

android:gravity="center_horizontal"/>

android:layout_height="wrap_content"

android:layout_weight="1"

android:drawableTop="@drawable/ic_collect"

android:text="收藏"

android:textColor="#ababab"

android:drawablePadding="10dp"

android:gravity="center_horizontal"/>

android:layout_height="wrap_content"

android:layout_weight="1"

android:drawableTop="@drawable/ic_comment"

android:text="评论 184"

android:textColor="#ababab"

android:drawablePadding="10dp"

android:gravity="center_horizontal"/>

其中的showAnim和dismissAnim是

showAnim = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.bottom_show);

dismissAnim = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.bottom_dismiss);

android:fromYDelta="10%p"

android:toYDelta="0"

/>

android:fromYDelta="0"

android:toYDelta="100%p"

/>

完整代码

好了现在所有的功能都已经实现了,博客将各个功能分开写了,是为了让大家清晰了解每个功能的实现方式,但是这样确实对于项目的完整性有一定的影响,我将代码提交到了我的GitHub中,大家可以到我的GitHub上下载完整代码,然后再配合博客中各个功能模块的讲解,希望对大家有所帮助

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值