原文地址为: Android实现局部图片滑动指引效果

  本文介绍在Android中实现局部的图片滑动指引效果。

  

  其实关于Android滑动指引效果,我曾经发布过一篇文章,里面实现的整个页面的指引滑动,有兴趣的朋友可以查看:http://www.cnblogs.com/hanyonglu/archive/2012/04/07/2435589.html。

 

  另外关于Android中的局部滑动,我也曾发布过一篇文章,是关于实现导航菜单的滑动,有兴趣的朋友可以查看:http://www.cnblogs.com/hanyonglu/archive/2012/04/21/2462311.html

 

  今天发布本文的原因是应一个网友要求,就是实现局部的图片滑动指引效果。这种效果一般是在新闻客户端上比较常见,其功能是:

  1、顶部单张图片左右拖拉滑动;

  2、带指引;

  3、仅滑动顶部单张图片,不滑动页面,下面的图文内容不动;

  4、类似于新闻客户端的功能

 

  为了大家能更好的理解,我们先来看下要实现的效果图:

   

    

    

 

  以上便是实现的效果图,其实实现原理也并不难,我们只需要将android-support-v4.jar包中ViewPager控件设置成局部就可以,只是处理界面时稍微有点麻烦,不过看完本篇之后,大家以后使用时直接调用就行。也希望本篇能够对大家有所帮助。

  好了,下面让我们开始我们的实现过程,主要给大家介绍一下实现步骤和一些核心代码。首先我们需要将android-support-v4.jar添加到工程当中,然后让我们看一下程序结构:

 

   

 

  我先简要介绍其实现原理:

  在布局页面中将设置成局部,限制其高度,然后为滑动的图片集合生成布局界面,并在代码中设置相应的数据适配器和监听事件。在切换事件监听器中更改相应的圆点图片和显示标题,由于滑动图片下方的界面不需要改变内容,所以很很容易内容超过屏幕,所以需要设置ScrollView以在内容比较多时显示滚动条,我会在下面介绍如何让ViewPager和ScrollView结合使用。

  

  先看下android.support.v4.view.ViewPager在布局界面中的核心代码:

<android.support.v4.view.ViewPager
android:id="@+id/image_slide_page"
android:layout_width
="fill_parent"
android:layout_height
="180dip"
android:focusable
="true" />

 

  在程序结构中,MainActivity.java是启动的Activity,而TopicNews.java是显示头条的Acitivity。在显示时,我们需要将TopicNews.java中的对象进行初始化设置,如下代码:

    /**
* 初始化
*/
private void initeViews(){
// 滑动图片区域
imagePageViews = new ArrayList();
LayoutInflater inflater
= getLayoutInflater();
main
= (ViewGroup)inflater.inflate(R.layout.page_topic_news, null);
viewPager
= (ViewPager) main.findViewById(R.id.image_slide_page);

// 圆点图片区域
parser = new NewsXmlParser();
int length = parser.getSlideImages().length;
imageCircleViews
= new ImageView[length];
imageCircleView
= (ViewGroup) main.findViewById(R.id.layout_circle_images);
slideLayout
= new SlideImageLayout(TopicNews.this);
slideLayout.setCircleImageLayout(length);

for(int i = 0;i < length;i++){
imagePageViews.add(slideLayout.getSlideImageLayout(parser.getSlideImages()[i]));
imageCircleViews[i]
= slideLayout.getCircleImageLayout(i);
imageCircleView.addView(slideLayout.getLinearLayout(imageCircleViews[i],
10, 10));
}

// 设置默认的滑动标题
tvSlideTitle = (TextView) main.findViewById(R.id.tvSlideTitle);
tvSlideTitle.setText(parser.getSlideTitles()[
0]);

setContentView(main);

// 设置ViewPager
viewPager.setAdapter(new SlideImageAdapter());
viewPager.setOnPageChangeListener(
new ImagePageChangeListener());
}

 

  以上对象的声明代码如下所示:

  // 滑动图片的集合
private ArrayList imagePageViews = null;
private ViewGroup main = null;
private ViewPager viewPager = null;
// 当前ViewPager索引
private int pageIndex = 0;

// 包含圆点图片的View
private ViewGroup imageCircleView = null;
private ImageView[] imageCircleViews = null;

// 滑动标题
private TextView tvSlideTitle = null;

// 布局设置类
private SlideImageLayout slideLayout = null;
// 数据解析类
private NewsXmlParser parser = null;

 

  由于在显示头条的Activity即TopicNews中,设置布局文件不是直接设置的,也就是通过inflate将Layout转化为View控件的,所以在使用page_topic_news.xml中的View时,需要通过main.findViewById(),即如下代码所示:

     main = (ViewGroup)inflater.inflate(R.layout.page_topic_news, null);
viewPager
= (ViewPager) main.findViewById(R.id.image_slide_page);

 

  而不能像这样直接使用:

        viewPager = (ViewPager) findViewById(R.id.image_slide_page); 

 

  这点大家在使用时需要注意。

  NewsXmlParser类是用于对显示的数据进行解析,由于本示例只是一个演示示例,所以在这个类里我只是设置一些要显示的固定数据,没有设置动态数据,这点明白就可以,代码如下:

package com.image.indicator.parser;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;

import android.util.Xml;

import com.image.indicator.R;
import com.image.indicator.entity.News;
import com.image.indicator.utility.FileAccess;

/**
* 解析新闻数据列表
* @Description: 解析新闻数据列表,这里只是个示例,具体地不再实现。

* @File: NewsXmlParser.java

* @Package com.image.indicator.parser

* @Author Hanyonglu

* @Date 2012-6-18 下午02:31:26

* @Version V1.0
*/
public class NewsXmlParser {
// 新闻列表
private List> newsList = null;

// 滑动图片的集合,这里设置成了固定加载,当然也可动态加载。
private int[] slideImages = {
R.drawable.image01,
R.drawable.image02,
R.drawable.image03,
R.drawable.image04,
R.drawable.image05};

// 滑动标题的集合
private int[] slideTitles = {
R.string.title1,
R.string.title2,
R.string.title3,
R.string.title4,
R.string.title5,
};

// 滑动链接的集合
private String[] slideUrls = {
"http://mobile.csdn.net/a/20120616/2806676.html",
"http://cloud.csdn.net/a/20120614/2806646.html",
"http://mobile.csdn.net/a/20120613/2806603.html",
"http://news.csdn.net/a/20120612/2806565.html",
"http://mobile.csdn.net/a/20120615/2806659.html",
};

public int[] getSlideImages(){
return slideImages;
}

public int[] getSlideTitles(){
return slideTitles;
}

public String[] getSlideUrls(){
return slideUrls;
}

/**
* 获取XmlPullParser对象
*
@param result
*
@return
*/
private XmlPullParser getXmlPullParser(String result){
XmlPullParser parser
= Xml.newPullParser();
InputStream inputStream
= FileAccess.String2InputStream(result);

try {
parser.setInput(inputStream,
"UTF-8");
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}

return parser;
}

public int getNewsListCount(String result){
int count = -1;

try {
XmlPullParser parser
= getXmlPullParser(result);
int event = parser.getEventType();//产生第一个事件

while(event != XmlPullParser.END_DOCUMENT){
switch(event){
case XmlPullParser.START_DOCUMENT:
break;
case XmlPullParser.START_TAG://判断当前事件是否是标签元素开始事件
if("count".equals(parser.getName())){//判断开始标签元素是否是count
count = Integer.parseInt(parser.nextText());
}

break;
case XmlPullParser.END_TAG://判断当前事件是否是标签元素结束事件
// if("count".equals(parser.getName())){//判断开始标签元素是否是count
// count = Integer.parseInt(parser.nextText());
// }

break;
}

event
= parser.next();//进入下一个元素并触发相应事件
}
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}

// 无返回值,则返回-1
return count;
}
}

 

  关于NewsXmlParser这个类,实现比较简单,不再详述,有兴趣的朋友可以在开发过程中将其设置成动态数据并进行解析。

 

  刚才在上面介绍其实现原理时,我提到需要设置滑动图片集合的布局界面,那么如何设置其布局呢?这里我们需要用到SlideImageLayout。

  SlideImageLayout类是用于生成滑动图片区域布局和圆点图片布局的类。我在上面的代码中(即在TopicNews.java的初始化方法initeViews())使用for循环设置滑动图片及圆点图片的布局。在循环中就用到了getSlideImageLayout()、getCircleImageLayout()和getLinearLayout()这几个方法。下面分别看下其功能,先看下getSlideImageLayout()实现代码:

    /**
* 生成滑动图片区域布局
*
@param index
*
@return
*/
public View getSlideImageLayout(int index){
// 包含TextView的LinearLayout
LinearLayout imageLinerLayout = new LinearLayout(activity);
LinearLayout.LayoutParams imageLinerLayoutParames
= new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT,
1);

ImageView iv
= new ImageView(activity);
iv.setBackgroundResource(index);
iv.setOnClickListener(
new ImageOnClickListener());
imageLinerLayout.addView(iv,imageLinerLayoutParames);
imageList.add(iv);

return imageLinerLayout;
}

   

  由于滑动图片一般需要设置其链接或是相应的ID,以便在点击时转向相应的Activity,显示相应的内容或详细信息。这里我没有过多的设置,只是在点击时显示标题及链接地址,代码如下:

    // 滑动页面点击事件监听器
private class ImageOnClickListener implements OnClickListener{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(activity, parser.getSlideTitles()[pageIndex], Toast.LENGTH_SHORT).show();
Toast.makeText(activity, parser.getSlideUrls()[pageIndex], Toast.LENGTH_SHORT).show();
}
}

 

  getCircleImageLayout()方法主要是为圆点图片生成相应的ImageView对象,代码如下:

    /**
* 生成圆点图片区域布局对象
*
@param index
*
@return
*/
public ImageView getCircleImageLayout(int index){
imageView
= new ImageView(activity);
imageView.setLayoutParams(
new LayoutParams(10,10));
imageView.setScaleType(ScaleType.FIT_XY);

imageViews[index]
= imageView;

if (index == 0) {
//默认选中第一张图片
imageViews[index].setBackgroundResource(R.drawable.dot_selected);
}
else {
imageViews[index].setBackgroundResource(R.drawable.dot_none);
}

return imageViews[index];
}

 

  getLinearLayout()方法则是为圆点图片添加相应的LinearLayout布局,以便设置圆点图片之间的距离,代码如下:

    /**
* 获取LinearLayout
*
@param view
*
@param width
*
@param height
*
@return
*/
public View getLinearLayout(View view,int width,int height){
LinearLayout linerLayout
= new LinearLayout(activity);
LinearLayout.LayoutParams linerLayoutParames
= new LinearLayout.LayoutParams(
width,
height,
1);
// 这里最好也自定义设置,有兴趣的自己设置。
linerLayout.setPadding(10, 0, 10, 0);
linerLayout.addView(view, linerLayoutParames);

return linerLayout;
}

 

  getCircleImageLayout()和getLinearLayout()方法在NewsTopic.java中for循环的结构中结合代码如下:

     imageCircleViews[i] = slideLayout.getCircleImageLayout(i);
imageCircleView.addView(slideLayout.getLinearLayout(imageCircleViews[i],
10, 10));

  

  这两个方法结合使用便能优美地实现其圆点图片的布局。

 

  以上是关于NewsXmlParser和SlideImageLayout两个类的介绍,下面让我们再回到TopicNews类中继续介绍相关知识。在TopicNews中进行对象初始化(initeViews()方法)以后,还需要设置ViewPager对象中的数据适配器和监听事件。ViewPager中数据适配器的代码如下:

  // 滑动图片数据适配器
private class SlideImageAdapter extends PagerAdapter {
@Override
public int getCount() {
return imagePageViews.size();
}

@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}

@Override
public int getItemPosition(Object object) {
// TODO Auto-generated method stub
return super.getItemPosition(object);
}

@Override
public void destroyItem(View arg0, int arg1, Object arg2) {
// TODO Auto-generated method stub
((ViewPager) arg0).removeView(imagePageViews.get(arg1));
}

@Override
public Object instantiateItem(View arg0, int arg1) {
// TODO Auto-generated method stub
((ViewPager) arg0).addView(imagePageViews.get(arg1));

return imagePageViews.get(arg1);
}

@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
// TODO Auto-generated method stub

}

@Override
public Parcelable saveState() {
// TODO Auto-generated method stub
return null;
}

@Override
public void startUpdate(View arg0) {
// TODO Auto-generated method stub

}

@Override
public void finishUpdate(View arg0) {
// TODO Auto-generated method stub

}
}

 

  而ViewPager的事件监听器代码如下:

  // 滑动页面更改事件监听器
private class ImagePageChangeListener implements OnPageChangeListener {
@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub

}

@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub

}

@Override
public void onPageSelected(int index) {
pageIndex
= index;
slideLayout.setPageIndex(index);
tvSlideTitle.setText(parser.getSlideTitles()[index]);

for (int i = 0; i < imageCircleViews.length; i++) {
imageCircleViews[index].setBackgroundResource(R.drawable.dot_selected);

if (index != i) {
imageCircleViews[i].setBackgroundResource(R.drawable.dot_none);
}
}
}
}

 

  事件监听器中主要在回调函数onPageSelected(int index)中变换标题和圆点图片。

 

  由于滑动区域下方的内容是不变的,也就是不滑动的,正如在我在上面提到的,内容可能会超出屏幕的范围,所以我们需要使用ScrollView以便内容过多的时候显示滚动条。可能一部分朋友会想到,要显示滚动条我也知道使用ScrollView。我想在这里说的是,这里即有ViewPager控件,也有ScrollView,如果两个View单独使用不会有什么问题。然而不幸的是,两个一结合使用就出现了问题。什么问题呢?就是在滑动图片时出现反弹的现象,就是在滑动时很难滑动,我滑动时感觉很吃力,而且图片就是滑动不过去,这个就是两个View之间的冲突,因为两个View都是滑动的View,都会计算相应的位置和判断相应的距离。

 

  我们如何来解决这个冲突呢?这里我们需要重写ScrollView的onInterceptTouchEvent()回调函数。需要在程序里新加一个ScrollViewExtend类并继承自ScrollView,下面是其代码:

package com.image.indicator.control;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ScrollView;

/**
* 能够兼容ViewPager的ScrollView
* @Description: 解决了ViewPager在ScrollView中的滑动反弹问题

* @File: ScrollViewExtend.java

* @Package com.image.indicator.control

* @Author Hanyonglu

* @Date 2012-6-18 下午01:34:50

* @Version V1.0
*/
public class ScrollViewExtend extends ScrollView {
// 滑动距离及坐标
private float xDistance, yDistance, xLast, yLast;

public ScrollViewExtend(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
xDistance
= yDistance = 0f;
xLast
= ev.getX();
yLast
= ev.getY();
break;
case MotionEvent.ACTION_MOVE:
final float curX = ev.getX();
final float curY = ev.getY();

xDistance
+= Math.abs(curX - xLast);
yDistance
+= Math.abs(curY - yLast);
xLast
= curX;
yLast
= curY;

if(xDistance > yDistance){
return false;
}
}

return super.onInterceptTouchEvent(ev);
}
}

 

  然后在我们的布局代码中添加这个扩展的View,如下代码:

<com.image.indicator.control.ScrollViewExtend
android:layout_width="match_parent"
android:layout_height
="fill_parent">

……

com.image.indicator.control.ScrollViewExtend>

 

  以上的操作便可解决ViewPager和ScrollView之间冲突问题,这样便可使用滚动条顺利显示下方不变的内容。在这里再次给大家说明一下,由于本示例只是个演示示例,所以在滑动图片的下方,我只是用了一张图片固定地显示头条Activity的下方。当然有需要的朋友,可以将其进行改造,将滑动图片的下方区域添加个ListView等View之类的以显示相应要求的信息。

 

  一些朋友可能会注意到,在滑动图片区域的下方有一段透明的效果,如下图所示:

 

  

 

  这个实现也不难,只是在相应的布局代码中添加background属性即可,如下:

android:background="#55000000"

 

  当然,透明度的设置有个范围,有兴趣的朋友到网上查找一下,这里不再详述。

 

  本示例除了实现Android局部图片滑动指引效果以外,还实现了上方导航菜单切换的效果,关于这个效果并不稀奇,因为网上有一些人已经实现该功能。不过在这里,我跟他们做不太一样的是,点击上方的新闻分类时灵敏度比较好,也就是说点中的概率比较大。因为上方的新闻分类文字比较小,要想点中有时不是件容易的事。下面简要说一下其实现过程及相应的代码。

  

  由于要在点击新闻类别时背景图片需要动画效果,所以我添加了一个类:ImageAnimatioin,用于处理图片移动时动画效果。其代码如下:

    /**
* 设置图像移动动画效果
*
@param v
*
@param startX
*
@param toX
*
@param startY
*
@param toY
*/
public static void SetImageSlide(View v, int startX, int toX, int startY, int toY) {
TranslateAnimation anim
= new TranslateAnimation(startX, toX, startY, toY);
anim.setDuration(
100);
anim.setFillAfter(
true);
v.startAnimation(anim);
}

 

  下面展示一下点击新闻类别时的事件监听器中的代码,因为在这个过程中需要计算移动图片的位置和切换下面的主体内容,如下:

    // 新闻分类事件监听器
private class ItemOnclickListener implements OnClickListener{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
itemWidth = findViewById(R.id.layout).getWidth();

switch (v.getId()) {
case R.id.tv_title_news:
ImageAnimatioin.SetImageSlide(tvSelectedItem, startX,
0, 0, 0);
startX
= 0;
tvSelectedItem.setText(R.string.title_news_category_tops);

// 显示头条信息
intent.setClass(MainActivity.this, TopicNews.class);
vNewsMain
= getLocalActivityManager().startActivity(
"TopicNews", intent).getDecorView();
break;
case R.id.tv_title_info:
ImageAnimatioin.SetImageSlide(tvSelectedItem, startX, itemWidth,
0, 0);
startX
= itemWidth;
tvSelectedItem.setText(R.string.title_news_category_info);

// 显示资讯信息
intent.setClass(MainActivity.this, InfoNews.class);
vNewsMain
= getLocalActivityManager().startActivity(
"InfoNews", intent).getDecorView();
break;
case R.id.tv_title_blog:
ImageAnimatioin.SetImageSlide(tvSelectedItem, startX, itemWidth
* 2, 0, 0);
startX
= itemWidth * 2;
tvSelectedItem.setText(R.string.title_news_category_blog);

// 显示博客信息
intent.setClass(MainActivity.this, BlogNews.class);
vNewsMain
= getLocalActivityManager().startActivity(
"BlogNews", intent).getDecorView();
break;
case R.id.tv_title_magazine:
ImageAnimatioin.SetImageSlide(tvSelectedItem, startX, itemWidth
* 3, 0, 0);
startX
= itemWidth * 3;
tvSelectedItem.setText(R.string.title_news_category_magazine);

// 显示杂志信息
intent.setClass(MainActivity.this, MagazineNews.class);
vNewsMain
= getLocalActivityManager().startActivity(
"MagazineNews", intent).getDecorView();
break;
case R.id.tv_title_domain:
ImageAnimatioin.SetImageSlide(tvSelectedItem, startX, itemWidth
* 4, 0, 0);
startX
= itemWidth * 4;
tvSelectedItem.setText(R.string.title_news_category_domain);
// 显示业界信息
intent.setClass(MainActivity.this, DomainNews.class);
vNewsMain
= getLocalActivityManager().startActivity(
"DomainNews", intent).getDecorView();
break;
case R.id.tv_title_more:
ImageAnimatioin.SetImageSlide(tvSelectedItem, startX, itemWidth
* 5, 0, 0);
startX
= itemWidth * 5;
tvSelectedItem.setText(R.string.title_news_category_more);

// 显示更多信息
intent.setClass(MainActivity.this, MoreNews.class);
vNewsMain
= getLocalActivityManager().startActivity(
"MoreNews", intent).getDecorView();
break;
default:
break;
}

// 更换Layout中的新闻主体
rlNewsMain.removeAllViews();
rlNewsMain.addView(vNewsMain, params);
}
}

 

  这里设置时是在一个主框架中变换新闻类别的布局,而不是直接显示,所以需要注意下。另外,在点击除头条之外的新闻类别时,下方展示的也只是个图片而已,有需要的朋友将其进行改造,这点不再多说。

  

  好了,不想再写下去了,以上便是本文的介绍过程,希望能给开发Android的朋友们带来帮助。

 

  示例下载:/Files/hanyonglu/AndroidFile/MyImageIndicator.rar 

 

  最后,希望转载的朋友能够尊重作者的劳动成果,加上转载地址:http://www.cnblogs.com/hanyonglu/archive/2012/06/19/2555113.html,谢谢。

 

  完毕。^_^

 


转载请注明本文地址: Android实现局部图片滑动指引效果

更多相关文章

  1. Android(安卓)M新控件之FloatingActionButton,TextInputLayout,Sna
  2. 关于android:layout_weight属性使用注意事项
  3. 关于android:layout_weight属性使用注意事项
  4. 关于android:layout_weight属性使用注意事项
  5. Android初学者学习之Android(安卓)屏幕适配2
  6. Android(安卓)异步加载图片分析
  7. Android(安卓)设置TextView字体Color Selector的正确方式
  8. Android(安卓)Permission访问权限大全
  9. 2011.10.14(2)——— android 仿照微信的图片展示功能 之 放大超过

随机推荐

  1. android studio 代码插件常用code整理
  2. android中的dp,px深度解析
  3. Android(安卓)Systrace工具
  4. Android框架LitePal
  5. Android换肤功能实现(白天、黑夜)
  6. 鸿洋博客精选
  7. android 预装第三方apk的方法
  8. 简述compileSdkVersion,minSdkVersion,targ
  9. unity3d android真机测试的2种方法
  10. Android(安卓)基础入门教程——开发环境