欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android仿小红书欢迎界面

程序员文章站 2023-12-03 22:57:46
1,觉得小红书的欢迎界面感觉很漂亮,就想来学习学习一下来实现类似于这种效果 。 原效果图如下: 2,根据效果我们来一点点分析 第一步:首先看一下我们的主界...

1,觉得小红书的欢迎界面感觉很漂亮,就想来学习学习一下来实现类似于这种效果 。 原效果图如下:

Android仿小红书欢迎界面

2,根据效果我们来一点点分析

第一步:首先看一下我们的主界面布局文件视图效果如下:

Android仿小红书欢迎界面

main_activity.xml文件代码如下:

<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:background="@color/white"
 android:orientation="vertical" >
<com.qianmo.xiaohongshuwelcome.parallaxpager.parallaxcontainer
 android:id="@+id/parallax_container"
 android:layout_width="match_parent"
 android:layout_height="match_parent"/>
 <imageview
 android:id="@+id/iv_man"
 android:layout_width="67dp"
 android:layout_height="202dp"
 android:layout_alignparentbottom="true"
 android:layout_centerhorizontal="true"
 android:layout_marginbottom="10dp"
android:background="@drawable/intro_item_manrun_1"
 android:visibility="visible" />
</relativelayout>

可以看到我们主界面的布局文件主要是两个控件,一个是包含上面小人行走效果的imageview,然后是一个自定义parallaxcontainer控件,这个自定义控件的具体是什么我们先不要管,后面再和大家来慢慢解释

第二步:看一下我们主界面的mainactivity的代码

mainactivity.java

package com.qianmo.xiaohongshuwelcome;
import android.app.activity;
import android.content.activitynotfoundexception;
import android.content.intent;
import android.net.uri;
import android.os.bundle;
import android.view.view;
import android.view.window;
import android.view.windowmanager;
import android.widget.imageview;
import com.qianmo.xiaohongshuwelcome.parallaxpager.parallaxcontainer;
/**
 * @author zhongdaxia 2014-12-15
 */
public class mainactivity extends activity {
 imageview iv_man;
 imageview rl_weibo;
 parallaxcontainer parallaxcontainer;
 @override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 //获取当前窗体
 final window window = getwindow();
 window.setflags(windowmanager.layoutparams.flag_fullscreen, windowmanager.layoutparams.flag_fullscreen);
 setcontentview(r.layout.activity_main);
 /**
 * 动画支持11以上sdk,11以下默认不显示动画
 * 若需要支持11以下动画,也可导入https://github.com/jakewharton/nineoldandroids
 */
 if (android.os.build.version.sdk_int > 10) {
 iv_man = (imageview) findviewbyid(r.id.iv_man);
 parallaxcontainer = (parallaxcontainer) findviewbyid(r.id.parallax_container);
 if (parallaxcontainer != null) {
 parallaxcontainer.setimage(iv_man);
 parallaxcontainer.setlooping(false);
 iv_man.setvisibility(view.visible);
 parallaxcontainer.setupchildren(getlayoutinflater(),
  r.layout.view_intro_1, r.layout.view_intro_2,
  r.layout.view_intro_3, r.layout.view_intro_4,
  r.layout.view_intro_5, r.layout.view_intro_6 ,r.layout.view_login);
 }
 }
 else{
 setcontentview(r.layout.view_login);
 }
 }
}

我们看到代码很简单,主要是这几句有用的代码:

if (parallaxcontainer != null) {
 parallaxcontainer.setimage(iv_man);
 parallaxcontainer.setlooping(false);
 iv_man.setvisibility(view.visible);
 parallaxcontainer.setupchildren(getlayoutinflater(),
  r.layout.view_intro_1, r.layout.view_intro_2,
  r.layout.view_intro_3, r.layout.view_intro_4,
  r.layout.view_intro_5, r.layout.view_intro_6 ,r.layout.view_login);
}

① 将我们小人走路的那个imageview添加到自定义控件parallaxcontainer中

② 将我们每一个的布局文件set到parallaxcontainer控件中去

这里给出r.layout.view_intro_1.xml文件代码,其他的类似,就不给出来了(这里的x_in、x_out、y_in等属性注意一下)

<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent" >
 <imageview
 android:id="@+id/iv_0"
 android:layout_width="103dp"
 android:layout_height="19dp"
 android:layout_centerinparent="true"
 android:src="@drawable/intro1_item_0"
 app:x_in="1.2"
 app:x_out="1.2" />
 <imageview
 android:id="@+id/iv_1"
 android:layout_width="181dp"
 android:layout_height="84dp"
 android:layout_alignparentleft="true"
 android:layout_alignparenttop="true"
 android:layout_marginleft="13dp"
 android:layout_margintop="60dp"
 android:src="@drawable/intro1_item_1"
 app:x_in="0.8"
 app:x_out="0.8" />
 <imageview
 android:id="@+id/iv_2"
 android:layout_width="143dp"
 android:layout_height="58dp"
 android:layout_alignparentright="true"
 android:layout_alignparenttop="true"
 android:layout_margintop="109dp"
 android:src="@drawable/intro1_item_2"
 app:x_in="1.1"
 app:x_out="1.1" />
 <imageview
 android:id="@+id/iv_3"
 android:layout_width="48dp"
 android:layout_height="48dp"
 android:src="@drawable/intro1_item_3"
 app:x_in="0.8"
 app:x_out="0.8"
 app:a_in="0.8"
 app:a_out="0.8"
 android:layout_below="@+id/iv_0"
 android:layout_torightof="@+id/iv_5"
 android:layout_toendof="@+id/iv_5"
 android:layout_marginleft="21dp"
 android:layout_marginstart="21dp"
 android:layout_margintop="12dp"/>
 <imageview
 android:id="@+id/iv_4"
 android:layout_width="fill_parent"
 android:layout_height="128dp"
 android:layout_alignparentbottom="true"
 android:layout_marginbottom="29dp"
 android:background="@drawable/intro1_item_4"
 app:a_in="0.8"
 app:a_out="0.8"
 app:x_in="0.8"
 app:x_out="0.8" />
 <imageview
 android:id="@+id/iv_5"
 android:layout_width="260dp"
 android:layout_height="18dp"
 android:layout_alignparentbottom="true"
 android:layout_alignparentleft="true"
 android:layout_marginbottom="16dp"
 android:layout_marginleft="15dp"
 android:src="@drawable/intro1_item_5"
 app:a_in="0.9"
 app:a_out="0.9"
 app:x_in="0.9"
 app:x_out="0.9" />
 <imageview
 android:id="@+id/iv_6"
 android:layout_width="24dp"
 android:layout_height="116dp"
 android:layout_alignparentbottom="true"
 android:layout_alignparentleft="true"
 android:layout_marginbottom="35dp"
 android:layout_marginleft="46dp"
 android:src="@drawable/intro1_item_6"
 app:x_in="0.6"
 app:x_out="0.6" />
 <imageview
 android:id="@+id/iv_7"
 android:layout_width="45dp"
 android:layout_height="40dp"
 android:layout_alignparentbottom="true"
 android:layout_alignparentleft="true"
 android:layout_marginbottom="23dp"
 android:layout_marginleft="76dp"
 android:src="@drawable/intro1_item_7"
 app:a_in="0.3"
 app:a_out="0.3"
 app:x_in="0.5"
 app:x_out="0.5" />
</relativelayout>

第三步:好了现在我们一定很好奇parallaxcontainer里面的内容,那我们从上面的方法慢慢去看,首先看一下parallaxcontainer中的setimage()方法,代码如下:

imageview iv;
//将小人图片添加进来
public void setimage(imageview iv) {
 this.iv = iv;
}

貌似没有什么,只是将它赋值给成员变量iv,我们接着看下一个setupchildren()方法

//添加子view
public void setupchildren(layoutinflater inflater, int... childids) {
 if (getchildcount() > 0) {
 throw new runtimeexception("setupchildren should only be called once when parallaxcontainer is empty");
 }
 //创建打气筒
 parallaxlayoutinflater parallaxlayoutinflater = new parallaxlayoutinflater(
 inflater, getcontext());

 //将所有的view添加到本控件上去
 for (int childid : childids) {
 view view = parallaxlayoutinflater.inflate(childid, this);
 viewlist.add(view);
 }
 //添加视觉view
 pagecount = getchildcount();
 for (int i = 0; i < pagecount; i++) {
 view view = getchildat(i);
 addparallaxview(view, i);
 }
 //更新viewpageradapter的数量
 updateadaptercount();
 //创建viewpager
 viewpager = new viewpager(getcontext());
 viewpager.setlayoutparams(new layoutparams(layoutparams.match_parent, layoutparams.match_parent));
 viewpager.setid(r.id.parallax_pager);
 //给viewpager添加滑动监听
 attachonpagechangelistener();
 //设置适配器
 viewpager.setadapter(adapter);
 //将viewpager添加到主控件中
 addview(viewpager, 0);
}

让我们一行行代码慢慢分析

if (getchildcount() > 0) {
 throw new runtimeexception("setupchildren should only be called once when parallaxcontainer is empty");
 }
//创建打气筒
 parallaxlayoutinflater parallaxlayoutinflater = new parallaxlayoutinflater(
 inflater, getcontext());

首先看一下这段代码只是if判断是否已经调用过setupchildren()方法,没什么重要的,在看创建parallaxlayoutinflater打气筒对象,我们来看看parallaxlayoutinflater的具体代码,没什么重要的,只是里面有一个parallaxfactory类我们没见过,留心一下!

package com.qianmo.xiaohongshuwelcome.parallaxpager;
import android.content.context;
import android.view.layoutinflater;
public class parallaxlayoutinflater extends layoutinflater {
 protected parallaxlayoutinflater(layoutinflater original, context newcontext) {
 super(original, newcontext);
 setuplayoutfactory();
 }
 private void setuplayoutfactory() {
 if (!(getfactory() instanceof parallaxfactory)) {
 setfactory(new parallaxfactory(this, getfactory()));
 }
 }
 @override
 public layoutinflater cloneincontext(context newcontext) {
 return new parallaxlayoutinflater(this, newcontext);
 }
}

再看下面一段代码,主要是将所有的布局文件添加到viewlist集合中去,并填充到我们的布局中,我们继续往下看

//将所有的view添加到本控件上去
 for (int childid : childids) {
 view view = parallaxlayoutinflater.inflate(childid, this);
 viewlist.add(view);
 }

下面一段代码主要是看拿到对应所有的子view,关键是我们的addparallaxview()方法,具体代码如下:

//添加视觉view
 pagecount = getchildcount();
 for (int i = 0; i < pagecount; i++) {
 view view = getchildat(i);
 addparallaxview(view, i);
 }
/**
 * 添加视觉view方法
 *
 * @param view
 * @param pageindex
 */
 private void addparallaxview(view view, int pageindex) {
 //通过递归方法拿到最小单元的view
 if (view instanceof viewgroup) {
 viewgroup viewgroup = (viewgroup) view;
 for (int i = 0, childcount = viewgroup.getchildcount(); i < childcount; i++) {
 addparallaxview(viewgroup.getchildat(i), pageindex);
 }
 }
 //创建视觉差view绑定,并添加到集合中去
 parallaxviewtag tag = (parallaxviewtag) view.gettag(r.id.parallax_view_tag);
 if (tag != null) {
 tag.index = pageindex;
 parallaxviews.add(view);
 }
 }

通过递归将每个布局文件中的最小单元view保存到parallaxview集合中去,但是等等,这里我们又发现了一个新的类parallaxviewtag,让我们来具体代码

package com.qianmo.xiaohongshuwelcome.parallaxpager;
public class parallaxviewtag {
 //绑定每一个view对应的是哪一个下标的
 protected int index;
 //x轴进入的速度
 protected float xin;
 protected float xout;
 protected float yin;
 protected float yout;
 protected float alphain;
 protected float alphaout;
}

貌似很简单,xin、xout貌似很熟悉和我们之前的布局文件属性app:x_in等属性对应了, 这样我们就懂了,这个类是相当于一个tag类,用于记录我们设置的特殊一些属性,然是我们这里有一个疑问,下面这个代码是get到tag,那我们是在哪里set里面的属性呢?

parallaxviewtag tag = (parallaxviewtag) view.gettag(r.id.parallax_view_tag);

这时候我们要看看我们前面提到过的一个陌生类parallaxfactory,看一下具体代码

package com.qianmo.xiaohongshuwelcome.parallaxpager;
import android.content.context;
import android.content.res.typedarray;
import android.util.attributeset;
import android.view.layoutinflater;
import android.view.view;
import com.qianmo.xiaohongshuwelcome.r;
public class parallaxfactory implements layoutinflater.factory {
 private final layoutinflater.factory factory;
 private parallaxlayoutinflater minflater;
 private static final string[] sclassprefixlist = {
 "android.widget.",
 "android.webkit.",
 "android.view."
 };
 public parallaxfactory(parallaxlayoutinflater inflater, layoutinflater.factory factory) {
 minflater = inflater;
 this.factory = factory;
 }
 @override
 public view oncreateview(string name, context context, attributeset attrs) {
 view view = null;
 if (context instanceof layoutinflater.factory) {
 view = ((layoutinflater.factory) context).oncreateview(name, context, attrs);
 }
 if (factory != null && view == null) {
 view = factory.oncreateview(name, context, attrs);
 }
 if (view == null) {
 view = createvieworfailquietly(name, context, attrs);
 }
 if (view != null) {
 onviewcreated(view, context, attrs);
 }
 return view;
 }
 protected view createvieworfailquietly(string name, context context, attributeset attrs) {
 if (name.contains(".")) {
 return createvieworfailquietly(name, null, context, attrs);
 }
 for (final string prefix : sclassprefixlist) {
 final view view = createvieworfailquietly(name, prefix, context, attrs);

 if (view != null) {
 return view;
 }
 }
 return null;
 }
 protected view createvieworfailquietly(string name, string prefix, context context,
attributeset attrs) {
 try {
 return minflater.createview(name, prefix, attrs);
 } catch (exception ignore) {
 return null;
 }
 }
 /**
 * 主要是在viewcreated的时候将tag和view绑定起来
 *
 * @param view
 * @param context
 * @param attrs
 */
 protected void onviewcreated(view view, context context, attributeset attrs) {
 int[] attrids =
 {r.attr.a_in, r.attr.a_out, r.attr.x_in, r.attr.x_out, r.attr.y_in, r.attr.y_out,};
 typedarray a = context.obtainstyledattributes(attrs, attrids);
 if (a != null) {
 if (a.length() > 0) {
 parallaxviewtag tag = new parallaxviewtag();
 tag.alphain = a.getfloat(0, 0f);
 tag.alphaout = a.getfloat(1, 0f);
 tag.xin = a.getfloat(2, 0f);
 tag.xout = a.getfloat(3, 0f);
 tag.yin = a.getfloat(4, 0f);
 tag.yout = a.getfloat(5, 0f);
 view.settag(r.id.parallax_view_tag, tag);
 }
 a.recycle();
 }
 }
}

主要看onviewcreated()方法,可以看到,这里我们将对应的每个属性的值都set到了我们parallaxviewtag中,我们接着看下面的代码,调用updateadaptercount()方法让适配器去更新adapter的数量,这里我们可以看到适配器是继承pageradapter类,用于viewpager的适配器,这里使用linkedlist来存储view,这个方法很好,赞一下

//更新viewpageradapter的数量
 updateadaptercount();
//具体代码
//被调用的时候好像是0
 private void updateadaptercount() {
 adapter.setcount(islooping ? integer.max_value : pagecount);
 }
//下面是adapter的具体代码
package com.qianmo.xiaohongshuwelcome.parallaxpager;
import android.content.context;
import android.support.v4.view.pageradapter;
import android.view.view;
import android.view.viewgroup;
import java.util.linkedlist;
import static android.view.viewgroup.layoutparams;
import static android.view.viewgroup.layoutparams.match_parent;
public class parallaxpageradapter extends pageradapter {
 private int count = 0;
 private final context context;
 private final linkedlist<view> recyclebin = new linkedlist<view>();
 public parallaxpageradapter(context context) {
 this.context = context;
 }
 public void setcount(int count) {
 this.count = count;
 }
 @override public int getcount() {
 return count;
 }
 @override public object instantiateitem(viewgroup container, int position) {
 view view;
 if (!recyclebin.isempty()) {
 view = recyclebin.pop();
 } else {
 view = new view(context);
 view.setlayoutparams(new layoutparams(match_parent, match_parent));
 }
 container.addview(view);
 return view;
 }
 @override public void destroyitem(viewgroup container, int position, object object) {
 view view = (view) object;
 container.removeview(view);
 recyclebin.push(view);
 }
 @override public boolean isviewfromobject(view view, object object) {
 return view.equals(object);
 }
}

我们继续往下看,后面的就是创建viewpager对象,并addview到主控件上,在attachonpagerchangelistener()方法中添加viewpager的滑动监听

/创建viewpager
viewpager = new viewpager(getcontext());
viewpager.setlayoutparams(new layoutparams(layoutparams.match_parent, layoutparams.match_parent));
viewpager.setid(r.id.parallax_pager);
//给viewpager添加滑动监听
attachonpagechangelistener();
//设置适配器
viewpager.setadapter(adapter);
//将viewpager添加到主控件中
addview(viewpager, 0);

这里我们在onpagescrollstatechanged()方法判断是否开启下面小人行走的动画,通过onpagescrolled()方法监听滑动的具体偏移量,通过view.settranslationx()方法来改变对应的属性

protected void attachonpagechangelistener() {
 mcommonpagechangelistener = new viewpager.onpagechangelistener() {
 /**
 * 此方法是在状态改变的时候调用,其中arg0这个参数
 有三种状态(0,1,2)。arg0 ==1的时辰默示正在滑动,arg0==2的时辰默示滑动完毕了,arg0==0的时辰默示什么都没做。
 * @param state
 */
 @override
 public void onpagescrollstatechanged(int state) {
 log.v(tag, "onpagescrollstatechanged" + state);
 iv.setbackgroundresource(r.drawable.man_run);
 final animationdrawable animationdrawable = (animationdrawable) iv.getbackground();
 switch (state) {
  case 0:
  //处于展示阶段
  finishanim(animationdrawable);
  break;
  case 1:
  //正在滑动
  isend = false;
  animationdrawable.start();
  break;
  case 2:
  //滑动完毕
  finishanim(animationdrawable);
  break;
 }
 }
 //判断是否还是在左边
 boolean isleft = false;
 /**
 * onpagescrolled(int arg0,float arg1,int arg2) ,当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为:
 * @param pageindex 当前页面,及你点击滑动的页面
 * @param offset 当前页面偏移的百分比
 * @param offsetpixels 当前页面偏移的像素位置
 */
 @override
 public void onpagescrolled(int pageindex, float offset, int offsetpixels) {
// log.v(tag, "onpagescrolled" + pageindex + " offset" + offset + " offsetpixels" + offsetpixels);
 if (offsetpixels < 10) {
  isleft = false;
 }
 if (pagecount > 0) {
  pageindex = pageindex % pagecount;
 }
 if (pageindex == 3) {
  if (isleft) {

  } else {
  iv.setx(iv.getleft() - offsetpixels);
  }
 }
 parallaxviewtag tag;
 for (view view : parallaxviews) {
  tag = (parallaxviewtag) view.gettag(r.id.parallax_view_tag);
  if (tag == null) {
  continue;
  }
  if ((pageindex == tag.index - 1 || (islooping && (pageindex == tag.index
  - 1 + pagecount)))
  && containerwidth != 0) {
  // make visible
  view.setvisibility(visible);
  // slide in from right
  view.settranslationx((containerwidth - offsetpixels) * tag.xin);
  // slide in from top
  view.settranslationy(0 - (containerwidth - offsetpixels) * tag.yin);
  // fade in
  view.setalpha(1.0f - (containerwidth - offsetpixels) * tag.alphain / containerwidth);
  } else if (pageindex == tag.index) {
  // make visible
  view.setvisibility(visible);
  // slide out to left
  view.settranslationx(0 - offsetpixels * tag.xout);
  // slide out to top
  view.settranslationy(0 - offsetpixels * tag.yout);
  // fade out
  view.setalpha(1.0f - offsetpixels * tag.alphaout / containerwidth);

  } else {
  view.setvisibility(gone);
  }
 }
 }
 @override
 public void onpageselected(int position) {
 log.v(tag, "onpageselected" + position);
 currentposition = position;
 }
 };
 viewpager.setonpagechangelistener(mcommonpagechangelistener);
 }

4,这里基本上就把源码分析完了,so,既然分析完别人的源码了下面就是结合到自己项目中去用了,当我们,想要实现一个翻页从顶部斜飞入的view,那我们的布局文件代码可以如下:

<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
android:background="@color/transparent">
 <imageview
 android:id="@+id/iv_2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignparenttop="true"
 android:layout_centerhorizontal="true"
 android:layout_marginleft="133dp"
 android:layout_margintop="39dp"
 android:src="@drawable/ic_launcher"
 app:x_in="1"
 app:x_out="1"
 app:y_in="0.6"
 app:y_out="0.9"/>
 <imageview
 android:id="@+id/iv_11"
 android:layout_width="44dp"
 android:layout_height="47dp"
 android:layout_alignparentbottom="true"
 android:layout_centerhorizontal="true"
 android:layout_marginbottom="66dp"
 android:layout_marginleft="140dp"
 android:src="@drawable/ic_launcher"
 app:x_in="1"
 app:x_out="1"
 app:y_in="-1.3"
 app:y_out="-1.3"/>
</relativelayout>

来看一下我们的效果:

Android仿小红书欢迎界面

这是github下载地址,由于要源码的同学可以去下载一下,see you next time !!!

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!