Android通过自定义控件实现360软件详情页效果

一、概述

最近有不少朋友私聊问应用宝、360软件助手之类的软件详情页怎么做,刚好,最近有时间就模仿360软件助手详情页给大家做个Demo,供大家参考。嗯,关于实现呢,我写了两种方式:

1、ScrollView内嵌软件介绍+ViewPager+ViewPager中是ScrollView,这种方式呢,纯原生,没有涉及到自定义控件,但是这样嵌套呢,涉及到测量以及事件的冲突处理,大家可以自己尝试去做一下,想像起来蛮容易的,做起来其实还是挺费劲的,代码我会给出,核心代码不多,大家自行参考。本文将重点分析第二种方式。

2、将做外层的ScrollView改为了自定义的一个控件,继承自LinearLayout,叫做StickyNavLayout,这里感谢小七的命名,同时本方式感谢二群暖暖提供的源码。

最后看下效果图,第一张是360的,第二张是我们的:

360:

擦,别问我为什么这么模糊,尽力了。。。

我们的效果图:

二、使用方式

上面我们也说了,之所以有第二种方式,完全是为了考虑使用的容易度。

1、自定义id资源文件

values/ids_sticky_nav_Llayout.xml

<?xml version="1.0" encoding="utf-8"?>
<resources> 

 <item name="id_stickynavlayout_topview" type="id"/>
 <item name="id_stickynavlayout_viewpager" type="id"/>
 <item name="id_stickynavlayout_indicator" type="id"/>
 <item name="id_stickynavlayout_innerscrollview" type="id"/> 

</resources> 

定义了几个id资源,主要是为了方便使用了,供声明布局时使用的,看名字应该能猜出来吧,猜不出来没事,接下来我就贴布局文件了。这个其实不属于使用方式了,但是下文会见到,所以提前贴出来。

2、布局文件

<com.zhy.view.StickyNavLayout xmlns:tools="http://schemas.android.com/tools"
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical" > 

 <RelativeLayout
  android:id="@id/id_stickynavlayout_topview"
  android:layout_width="match_parent"
  android:layout_height="300dp"
  android:background="#4400ff00" > 

  <TextView
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:gravity="center"
   android:text="软件介绍"
   android:textSize="30sp"
   android:textStyle="bold" />
 </RelativeLayout> 

 <com.zhy.view.SimpleViewPagerIndicator
  android:id="@id/id_stickynavlayout_indicator"
  android:layout_width="match_parent"
  android:layout_height="50dp"
  android:background="#ffffffff" >
 </com.zhy.view.SimpleViewPagerIndicator> 

 <android.support.v4.view.ViewPager
  android:id="@id/id_stickynavlayout_viewpager"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:background="#44ff0000" >
 </android.support.v4.view.ViewPager> 

</com.zhy.view.StickyNavLayout> 

最外层是我们的自定义的控件StickyNavLayout,然后是顶部内容区域,Vp的指示器,ViewPager。按照效果图,去写就ok,注意部分id使用我们预设定的id资源。因为我们的StickyNavLayout需要通过id找到该控件,去进行一些计算。
然后在我们的MainActivity中,对ViewPager进行初始化即可。

3、MainActivity

package com.zhy.sample.StickyNavLayout; 

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener; 

import com.zhy.view.SimpleViewPagerIndicator; 

public class MainActivity extends FragmentActivity
{
 private String[] mTitles = new String[] { "简介", "评价", "相关" };
 private SimpleViewPagerIndicator mIndicator;
 private ViewPager mViewPager;
 private FragmentPagerAdapter mAdapter;
 private TabFragment[] mFragments = new TabFragment[mTitles.length]; 

 @Override
 protected void onCreate(Bundle savedInstanceState)
 {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main); 

  initViews();
  initDatas();
  initEvents();
 } 

 private void initEvents()
 {
  mViewPager.setOnPageChangeListener(new OnPageChangeListener()
  {
   @Override
   public void onPageSelected(int position)
   {
   } 

   @Override
   public void onPageScrolled(int position, float positionOffset,
     int positionOffsetPixels)
   {
    mIndicator.scroll(position, positionOffset);
   } 

   @Override
   public void onPageScrollStateChanged(int state)
   { 

   }
  }); 

 } 

 private void initDatas()
 {
  mIndicator.setTitles(mTitles); 

  for (int i = 0; i < mTitles.length; i++)
  {
   mFragments[i] = (TabFragment) TabFragment.newInstance(mTitles[i]);
  } 

  mAdapter = new FragmentPagerAdapter(getSupportFragmentManager())
  {
   @Override
   public int getCount()
   {
    return mTitles.length;
   } 

   @Override
   public Fragment getItem(int position)
   {
    return mFragments[position];
   } 

  }; 

  mViewPager.setAdapter(mAdapter);
  mViewPager.setCurrentItem(0);
 } 

 private void initViews()
 {
  mIndicator = (SimpleViewPagerIndicator) findViewById(R.id.id_stickynavlayout_indicator);
  mViewPager = (ViewPager) findViewById(R.id.id_stickynavlayout_viewpager);
 } 

} 

没有什么复杂的代码,主要就是初始化我们的Vp;

对了这个指示器我是临时写的,也算一个自定义控件吧,主要就是跟随Vp一起移动,只是把三角形改成了下划线。

我们的Vp中每个页面为一个Fragment,Fragment的代码我们就不贴了,布局就是ScrollView为根布局,内部随意填充,具体可参考源码。

介绍完了用法有木有一点小激动,基本按照常规去写布局就ok,效果自动实现。

4、Fragment及其布局

贴一下我们的Fragment代码:

package com.zhy.sample.StickyNavLayout; 

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; 

public class TabFragment extends Fragment
{
 public static final String TITLE = "title";
 private String mTitle = "Defaut Value";
 private TextView mTextView; 

 @Override
 public void onCreate(Bundle savedInstanceState)
 {
  super.onCreate(savedInstanceState);
  if (getArguments() != null)
  {
   mTitle = getArguments().getString(TITLE);
  }
 } 

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState)
 {
  View view = inflater.inflate(R.layout.fragment_tab, container, false);
  mTextView = (TextView) view.findViewById(R.id.id_info);
  mTextView.setText(mTitle);
  return view; 

 } 

 public static TabFragment newInstance(String title)
 {
  TabFragment tabFragment = new TabFragment();
  Bundle bundle = new Bundle();
  bundle.putString(TITLE, title);
  tabFragment.setArguments(bundle);
  return tabFragment;
 } 

} 
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@id/id_stickynavlayout_innerscrollview"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:scrollbars="none" > 

 <LinearLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:background="#eee"
  android:orientation="vertical"
  android:padding="5dp" > 

  <TextView
   android:id="@+id/id_info"
   android:layout_width="match_parent"
   android:layout_height="50dp"
   android:background="#ffffffff"
   android:gravity="center" >
  </TextView>
 //省略了无数控件
 </LinearLayout> 

</ScrollView> 

没撒说的 ,let's go 。

三、StickyNavLayout源码剖析

1、构造

public class StickyNavLayout extends LinearLayout
{ 

 private View mTop;
 private View mNav;
 private ViewPager mViewPager; 

 private int mTopViewHeight;
 private ScrollView mInnerScrollView;
 private boolean isTopHidden = false; 

 private OverScroller mScroller;
 private VelocityTracker mVelocityTracker;
 private int mTouchSlop;
 private int mMaximumVelocity, mMinimumVelocity; 

 private float mLastY;
 private boolean mDragging; 

 public StickyNavLayout(Context context, AttributeSet attrs)
 {
  super(context, attrs);
  setOrientation(LinearLayout.VERTICAL); 

  mScroller = new OverScroller(context);
  mVelocityTracker = VelocityTracker.obtain();
  mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
  mMaximumVelocity = ViewConfiguration.get(context)
    .getScaledMaximumFlingVelocity();
  mMinimumVelocity = ViewConfiguration.get(context)
    .getScaledMinimumFlingVelocity(); 

 } 

 @Override
 protected void onFinishInflate()
 {
  super.onFinishInflate();
  mTop = findViewById(R.id.id_stickynavlayout_topview);
  mNav = findViewById(R.id.id_stickynavlayout_indicator);
  View view = findViewById(R.id.id_stickynavlayout_viewpager);
  if (!(view instanceof ViewPager))
  {
   throw new RuntimeException(
     "id_stickynavlayout_viewpager show used by ViewPager !");
  }
  mViewPager = (ViewPager) view;
 } 

ok,首先看下成员变量,和我们的变量的初始化,mTop、mNav、mViewPager代表我们布局的三大块了,初始化在onFinishInflate中完成,可以看到直接通过我们的id资源读取就ok。接下来还有些mScroller、mVelocityTracker、mTouchSlop、mMaximumVelocity、mMinimumVelocity、mLastY、mDragging,不用说大家都能想到这是和移动相关的,OverScroller是个辅助类,用于自定移动时帮我们处理掉数学的计算部分。mVelocityTracker相关几个变量,当然是计算什么时候需要自动移动;mTouchSlop帮我区别用户是点击还是拖拽。Android中封装了很多常量在ViewConfiguration中,大家有兴趣可以了解他,之所以使用这些常量不仅仅是说,省的我们自己去定义,而是为了和系统的行为保持一致。

看完了构造以后,由于我们使用的是LinearLayout,直接setOrientation(LinearLayout.VERTICAL);也就不必去layout了,控件都自定垂直排列了。那么我们在onMeasure中还需要做些处理。

2、onMeasure

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
 {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  ViewGroup.LayoutParams params = mViewPager.getLayoutParams();
  params.height = getMeasuredHeight() - mNav.getMeasuredHeight();
 } 

 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh)
 {
  super.onSizeChanged(w, h, oldw, oldh);
  mTopViewHeight = mTop.getMeasuredHeight();
 } 

主要是去设置ViewPager的高度,给它一个固定值,ViewPager自己在测量自己的时候,你要是不给它固定值,可能测量结果与你的预期会差距很大。比如你给它设置个WRAP_CONTENT,你希望他去计算孩子的高度去设置自己的,那么你就想多了。

大家可以看下ViewPager测量的源码:

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  // For simple implementation, our internal size is always 0.
  // We depend on the container to specify the layout size of
  // our view. We can't really know what it is since we will be
  // adding and removing different arbitrary views and do not
  // want the layout to change as this happens.
  setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
    getDefaultSize(0, heightMeasureSpec));
 }
 public static int getDefaultSize(int size, int measureSpec) {
  int result = size;
  int specMode = MeasureSpec.getMode(measureSpec);
  int specSize = MeasureSpec.getSize(measureSpec); 

  switch (specMode) {
  case MeasureSpec.UNSPECIFIED:
   result = size;
   break;
  case MeasureSpec.AT_MOST:
  case MeasureSpec.EXACTLY:
   result = specSize;
   break;
  }
  return result;
 } 

可以看到,对于AT_MOST和EXACTLY两种模式,都是直接读取父类的传入的测量值,也就是说,他不会去测量自己孩子的高度。然后如果模式是:UNSPECIFIED,那么高度直接为0呢。这里大家如果做过这个例子,应该能遇到这种情况,ScrollView中放ViewPager时,测量模式就是UNSPECIFIED,那么Vp直接不显示,原因就在这里。

扯远了,回来,我们继续。

我们设置为Vp的值以后,理论上来说,我们的显示已经正常了,控件都按照我们的预期显示了,但是,但是什么呢?我们现在在自定义的LinearLayout,那么移动是不是应该我们自己去写。

移动的代码很简单,想必大家都知道,直接拿到dx,然后scrollBy就行了。

3、onTouchEvent

@Override
public boolean onTouchEvent(MotionEvent event)
{
 mVelocityTracker.addMovement(event);
 int action = event.getAction();
 float y = event.getY(); 

 switch (action)
 {
 case MotionEvent.ACTION_DOWN:
  if (!mScroller.isFinished())
   mScroller.abortAnimation();
  mVelocityTracker.clear();
  mVelocityTracker.addMovement(event);
  mLastY = y;
  return true;
 case MotionEvent.ACTION_MOVE:
  float dy = y - mLastY; 

  if (!mDragging && Math.abs(dy) > mTouchSlop)
  {
   mDragging = true;
  }
  if (mDragging)
  {
   scrollBy(0, (int) -dy);
   mLastY = y;
  }
  break;
 case MotionEvent.ACTION_CANCEL:
  mDragging = false;
  if (!mScroller.isFinished())
  {
   mScroller.abortAnimation();
  }
  break;
 case MotionEvent.ACTION_UP:
  mDragging = false;
  mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
  int velocityY = (int) mVelocityTracker.getYVelocity();
  if (Math.abs(velocityY) > mMinimumVelocity)
  {
   fling(-velocityY);
  }
  mVelocityTracker.clear();
  break;
 } 

 return super.onTouchEvent(event);
} 

比较简单哈,我们因为只需要判断y方向的,所以down的时候记录下y的值,然后move的时候拿到dy,直接去进去scrollBy就好,当然我们在整个过程中.addMovement(event);所以,up的时候,我们得到v方向的velocityY,调用fling进行移动。

还好fling的核心代码OverScroller给我们实现了,so nice。

大家应该清楚,我们使用Scroller这样的辅助类时,它们帮我们完成的,知识数学方面的计算,至于自动我们还是需要自己去干的。

那怎么干,在哪干?这个无非就是重写computeScroll方法,在里面判断scroller是否结束,如果没有,则scrollTo一下,最后记得invalidate,相关代码:

public void fling(int velocityY)
 {
  mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);
  invalidate();
 } 

 @Override
 public void scrollTo(int x, int y)
 {
  if (y < 0)
  {
   y = 0;
  }
  if (y > mTopViewHeight)
  {
   y = mTopViewHeight;
  }
  if (y != getScrollY())
  {
   super.scrollTo(x, y);
  } 

  isTopHidden = getScrollY() == mTopViewHeight; 

 } 

 @Override
 public void computeScroll()
 {
  if (mScroller.computeScrollOffset())
  {
   scrollTo(0, mScroller.getCurrY());
   invalidate();
  }
 } 

ok,到此,我们的onTouchEvent搞定了~~but,别得意,为什么这么说呢?因为你完成的,知识当然View对于上下拖动的处理。大家别忘了,我们当前的StickyNavLayout内部可是有一个ScrollView的,那么根据事件的转发机制,这个内部的ScrollView肯定会处理上下拖动这种情况的,也就是我们的事件会被它拦截。

4、onInterceptTouchEvent

好了,接下来我们要处理拦截,对于拦截,我们要清楚的知道什么时候应该拦截,什么时候不需要,当前我们的例子:

1、如果我们的顶部view只要没有完全隐藏,那么直接拦截上下的拖动;

2、还有个需要拦截的地方,就是当顶部的view彻底隐藏了,我们现在内部的sc应该可以上下滑动了,但是如果sc滑动到顶部再往下的时候,此时又该拦截了,我们需要把顶部view可以下滑出来。

分析完成以后,看代码,这叫一个酸爽:

@Override
 public boolean onInterceptTouchEvent(MotionEvent ev)
 {
  int action = ev.getAction();
  float y = ev.getY(); 

  switch (action)
  {
  case MotionEvent.ACTION_DOWN:
   mLastY = y;
   break;
  case MotionEvent.ACTION_MOVE:
   float dy = y - mLastY; 

   getCurrentScrollView(); 

   if (Math.abs(dy) > mTouchSlop)
   {
    mDragging = true;
    if (!isTopHidden
      || (mInnerScrollView.getScrollY() == 0 && isTopHidden && dy > 0))
    {
     return true;
    }
   }
   break;
  }
  return super.onInterceptTouchEvent(ev);
 } 

ok,move中判断上述两种情况,o了。

源码下载:

github下载地址:https://github.com/hongyangAndroid/Android-StickyNavLayout

本地下载地址:http://xiazai.jb51.net/201705/yuanma/Android-StickyNavLayout(jb51.net).rar

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Android SDK Manager更新、下载速度慢问题解决办法

    Android SDK Manager更新.下载速度慢问题解决办法 解决Android SDK Manager更新.下载速度慢 打开目录 C:\Windows\System32\drivers\etc,在目录下有hosts文件 打开文件添加如下文本 203.208.46.146 www.google.com 74.125.113.121 developer.android.com 203.208.46.146 dl.google.com 203.208.46.146 dl-ssl.google.

  • Android 使用<layer-list>实现微信聊天输入框功能

    LayerDrawable  <layer-list> 标签可是设置LayerDrawable,一种有层次的Drawable叠加效果,<layer-list> 可以包含多个 <item>标签, 每个 <item>代表一个Drawable.<item>可以通过left.right.top.bottom设置左右上下的偏移量,<item>可以应用一个图片,也可以是一个shape 我们来模仿实现微信的聊天输入框: 先设置绿色的背景: <

  • android中强制更新app实例代码

    推荐第三种方式,简单快捷不卡. 第一种:jjdxm_update GitHub地址:jjdxmashl/jjdxm_update 效果图: 点击立即更新,程序会在后台下载,通知栏有下载进度.这个时候手机系统很卡,可能由于是下载app的原因吧.下载完成后弹出安装界面 简介: 这是大神jjdxmashl的开源项目,下载地址见上方.有版本更新.手动更新.静默更新.自动更新4种情况.应用内更新,实现类是友盟自动更新sdk的模式,用户使用前只需要配置自己的服务器更新检查接口即可(必须接口),也可以扩展加入

  • Android 属性动画ValueAnimator与插值器详解

    Android 属性动画ValueAnimator与插值器详解 一.ValueAnimator详解: ValueAnimator是整个动画的核心,ObjectAnimator即是继承自ValueAnimator来实现. ValueAnimator更像是一个数值发生器,用来产生具有一定规律的数字,从而让调动者来控制动画的实现过程. 1.ValueAnimator的使用: ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 100); val

  • Android自定义view实现太极效果实例代码

    Android自定义view实现太极效果实例代码 之前一直想要个加载的loading.却不知道用什么好,然后就想到了太极图标,最后效果是有了,不过感觉用来做loading简直丑到爆!!! 实现效果很简单,我们不要用什么贝塞尔曲线啥的,因为太极无非就是圆圆圆,只要画圆就ok了.来上代码: 因为有黑有白,所以定义2个画笔分别为黑和白. private void inital() { whitePaint = new Paint(); whitePaint.setAntiAlias(true); wh

  • 详解Android中的NestedScrolling机制带你玩转嵌套滑动

    一.概述 Android在support.v4包中为大家提供了两个非常神奇的类: NestedScrollingParent NestedScrollingChild 如果你从未听说过这两个类,没关系,听我慢慢介绍,你就明白这两个类可以用来干嘛了.相信大家都见识过或者使用过CoordinatorLayout,通过这个类可以非常便利的帮助我们完成一些炫丽的效果,例如下面这样的: 这样的效果就非常适合使用NestedScrolling机制去完成,并且CoordinatorLayout背后其实也是利用

  • Android中Progress的简单实例

    Android中Progress的简单实例 Android中Progress网上的demo都是瞎扯淡,当然,你们也可以认为我的demo是瞎扯淡,因为,毕竟要理解别人的思路,很头疼,主要还是知道思路,然后一步一步慢慢来.今天我讲的是Progress的实现,如果看了我之前的博客,应该多少有些了解.话不多说,来看实例: xml东西不多,自己定义把,因为Progress包括了一级读取和二级读取,所以主要是根据按钮来实现,下面就是一个点击事件: public void onClick(View v) {

  • Android中Edittext设置输入条件

    一.应用场景 之前做商城应用时,会有对用户资料的设置情况进行限制,如下: (1)用户邮箱,应当只允许输入英文字母,数字和@.两个符号, (2)用户手机,应当只能输入数字,禁止输入其他字符. (3)用户密码,应当不能输入空格以及中文字符等. 二.解决方案 针对用户邮箱以及用户手机,有以下两种解决方案: (1)EditText中的android:digits属性: 它表示EditText能够接受的字符集合.通过配置此属性,就可以实现只允许输入指定的字符.这里列举上述三种场景下的android:dig

  • Android通过自定义控件实现360软件详情页效果

    一.概述 最近有不少朋友私聊问应用宝.360软件助手之类的软件详情页怎么做,刚好,最近有时间就模仿360软件助手详情页给大家做个Demo,供大家参考.嗯,关于实现呢,我写了两种方式: 1.ScrollView内嵌软件介绍+ViewPager+ViewPager中是ScrollView,这种方式呢,纯原生,没有涉及到自定义控件,但是这样嵌套呢,涉及到测量以及事件的冲突处理,大家可以自己尝试去做一下,想像起来蛮容易的,做起来其实还是挺费劲的,代码我会给出,核心代码不多,大家自行参考.本文将重点分析第

  • Android利用Hero实现列表与详情页无缝切换动画

    目录 前言 思路 列表元素 详情页面 源码 总结 前言 介绍了几篇 Hero 动画,我们来一个 Hero 动画应用案例.在一些应用中,列表的元素和详情的内容是一致的,这个时候利用 Hero 动画切换到详情会感觉无缝过渡,用户体验会更好.例如本篇我们要实现下面的效果: Hero 应用:列表与详情切换 思路 上面的效果是列表和详情共用了头像和头像的背景色.二者的组合是一个 Stack 组件,因此可以使用 Hero 组件完成.然后是 Hero 组件的移动,我们先做了水平移动,再做垂直方向移动,这样过渡

  • Android实现绕球心旋转的引导页效果

    现在很多APP都会出现Android实现绕球心旋转的引导页效果,一个类似小车一直在往前开的旋转式动画效果. 先看一下预览效果: 嗯,整体效果还算理想,基本实现了页面绕屏幕底部中心旋转. 这里我们用到了Android系统的一个组件ViewFlipper,该控件的主要作用是为其中的View切换提供动画效果,主要的方法如下: setInAnimation:设置View进入屏幕时的动画. setOutAnimation:设置View退出屏幕时的动画. showNext:调用该方法可以显示下一个View.

  • Android自定义LinearLayout实现淘宝详情页

    1.简单说明 淘宝详情页就不用我一一介绍了,昨天逛淘宝看到这个效果时,让我想起了去年刚学习Android只会使用现成的时候,当时在网上找了一个这种效果的使用了,并不懂怎么实现的.现在就看到一种效果就想自己实现一下,我想这就是刚接触某个知识时的好奇心吧 说走咱就走啊,本文只是介绍一种实现思路,网上也已经有了很多种实现方式,有问题请指正 效果图(我有很用心的找美女图的) 2.实现思路 继承LinearLayout,设置方向为垂直 控件中有两个ScrollView,至于为什么要使用ScrollView

  • Android模仿知乎的回答详情页的动画效果

    废话不多说,咱们第一篇文章就是模仿"知乎"的回答详情页的动画效果,先上个原版的效果图,咱们就是要做出这个效果 在实现之前,我们先根据上面的动画效果,研究下需求,因为gif帧数有限,所以不是很连贯,推荐你直接下载一个知乎,找到这个界面自己玩玩 ☞当文章往上移动到一定位置之后,最上面的标题栏Bar和问题布局Title是会隐藏的,回答者Author布局不会隐藏 ☞当文章往下移动移动到一定位置之后,原先隐藏的标题栏Bar和问题布局Title会下降显示 ☞当文章往上移动的时候,下部隐藏的Tool

  • Android仿淘宝商品详情页效果

    本文实例为大家分享了Android仿淘宝商品详情页的具体代码,供大家参考,具体内容如下 Demo地址:先上效果图 效果就是上面图片的效果 接下来看看如何实现 首先我们来看下布局文件 <LinearLayout android:id="@+id/header" android:layout_width="match_parent" android:layout_height="72dp" android:paddingTop="24

  • Android通过手势实现答题器翻页效果

    本文实例为大家分享了Android答题器翻页功能,主要使用ViewFilpper和GestureDetector来实现,供大家参考,具体内容如下 1.效果图 2.实现思路 把Activity的TouchEvent事件交个GestureDetector来处理,然后使用ViewFilpper使用动画控制多个组件的之间的切换效果.手势的一个Api就不详细说了,大家如果不了解可以查一下. 3.实现的步骤 1).构建手势检测器 2).准备数据 3).为ViewFilpper添加子控件. 4).初始化Ani

  • Android自定义左右或上下滑动翻页效果

    本文实例为大家分享了Android自定义左右或上下滑动翻页展示的具体代码,供大家参考,具体内容如下 该自定义的效果和ViewPage+Fragment差不多 上下滑动翻页,继承ScrollView public class SlideScrollView extends ScrollView implements PageSlide{ private TotalSlide totalSlide; public SlideScrollView(AppCompatActivity context)

  • Android仿京东、天猫商品详情页

    前言 前面在介绍控件TabLayout控件和CoordinatorLayout使用的时候说了下实现京东.天猫详情页面的效果,今天要说的是优化版,是我们线上实现的效果,首先看一张效果: 项目结构分析 首先我们来分析一下要实现上面的效果,我们需要怎么做.顶部是一个可以滑动切换Tab,可以用ViewPager+Fragment实现,也可以使用系统的TabLayout控件实现:而下面的 View是一个可以滑动拖动效果的View,可以采用网上一个叫做DragLayout的控件,我这里是自己实现了一个,主要

  • 安卓(android)仿电商app商品详情页按钮浮动效果

    1.效果图如下: 这效果用户体验还是很酷炫,今天我们就来讲解如何实现这个效果. 2.分析 为了方便理解,作图分析 如图所示,整个页面分为四个部分: 1.悬浮内容,floatView 2.顶部内容,headView 3.中间内容,与悬浮内容相同,middleView 4.商品详情展示页面,detailView 因为页面内容高度会超出屏幕,所以用Scrollview实现滚动,悬浮view与scrollview同级,都在一个帧布局或者相对布局中. 当y方向的滚动距离小于中间的内容middleView到

随机推荐