在android中实现类似uc和墨迹天气的左右拖动效果
程序员文章站
2024-01-27 10:51:16
复制代码 代码如下: import android.app.activity; import android.os.bundle; import android.conte...
复制代码 代码如下:
import android.app.activity;
import android.os.bundle;
import android.content.context;
import android.graphics.color;
import android.util.log;
import android.view.gravity;
import android.view.motionevent;
import android.view.view;
import android.view.viewgroup;
import android.view.view.onclicklistener;
import android.widget.arrayadapter;
import android.widget.button;
import android.widget.checkbox;
import android.widget.edittext;
import android.widget.linearlayout;
import android.widget.tablelayout;
import android.widget.textview;
public class flinggalleryactivity extends activity
{
private final int color_red = color.argb(100, 200, 0, 0);
private final int color_green = color.argb(100, 0, 200, 0);
private final int color_blue = color.argb(100, 0, 0, 200);
private final int color_yellow = color.argb(100, 200, 200, 0);
private final int color_purple = color.argb(100, 200, 0, 200);
private final string[] mlabelarray = {"view1", "view2", "view3", "view4", "view5"};
private final int[] mcolorarray = {color_red, color_green, color_blue, color_yellow, color_purple};
private flinggallery mgallery;
private checkbox mcheckbox;
// note: the following handler is critical to correct function of
// the flinggallery class. this enables the flinggallery class to
// detect when the motion event has ended by finger being lifted
@override
public boolean ontouchevent(motionevent event)
{
return mgallery.ongallerytouchevent(event);
}
public void oncreate(bundle savedinstancestate)
{
super.oncreate(savedinstancestate);
mgallery = new flinggallery(this);
mgallery.setpaddingwidth(5);
mgallery.setadapter(new arrayadapter<string>(getapplicationcontext(), android.r.layout.simple_list_item_1, mlabelarray)
{
@override
public view getview(int position, view convertview, viewgroup parent)
{
log.d("111", "count="+position);
// if (convertview != null && convertview instanceof galleryviewitem)
// {
// galleryviewitem galleryview = (galleryviewitem) convertview;
//
// galleryview.medit1.settext("");
// galleryview.mtext1.settext(mlabelarray[position]);
// galleryview.mtext1.setbackgroundcolor(mcolorarray[position]);
// galleryview.mtext2.settext(mlabelarray[position]);
// galleryview.mtext2.setbackgroundcolor(mcolorarray[position]);
//
// log.d("111", "count="+position);
//
// return galleryview;
//
// }
return new galleryviewitem(getapplicationcontext(), position);
}
});
linearlayout layout = new linearlayout(getapplicationcontext());
layout.setorientation(linearlayout.vertical);
linearlayout.layoutparams layoutparams = new linearlayout.layoutparams(
linearlayout.layoutparams.match_parent,
linearlayout.layoutparams.match_parent);
layoutparams.setmargins(10, 10, 10, 10);
layoutparams.weight = 1.0f;
layout.addview(mgallery, layoutparams);
mcheckbox = new checkbox(getapplicationcontext());
mcheckbox.settext("gallery is circular");
mcheckbox.settext("gallery is circular");
mcheckbox.setpadding(50, 10, 0, 10);
mcheckbox.settextsize(30);
mcheckbox.setchecked(true);
mcheckbox.setonclicklistener(new onclicklistener()
{
@override
public void onclick(view view)
{
mgallery.setisgallerycircular(mcheckbox.ischecked());
}
});
layout.addview(mcheckbox, new linearlayout.layoutparams(
linearlayout.layoutparams.match_parent,
linearlayout.layoutparams.wrap_content));
setcontentview(layout);
}
private class galleryviewitem extends tablelayout
{
private edittext medit1;
private textview mtext1;
private textview mtext2;
private button mbutton1;
private button mbutton2;
public galleryviewitem(context context, int position)
{
super(context);
this.setorientation(linearlayout.vertical);
this.setlayoutparams(new linearlayout.layoutparams(
linearlayout.layoutparams.match_parent,
linearlayout.layoutparams.match_parent));
medit1 = new edittext(context);
this.addview(medit1, new linearlayout.layoutparams(
linearlayout.layoutparams.match_parent,
linearlayout.layoutparams.wrap_content));
mtext1 = new textview(context);
mtext1.settext(mlabelarray[position]);
mtext1.settextsize(30);
mtext1.setgravity(gravity.left);
mtext1.setbackgroundcolor(mcolorarray[position]);
this.addview(mtext1, new linearlayout.layoutparams(
linearlayout.layoutparams.match_parent,
linearlayout.layoutparams.wrap_content));
mbutton1 = new button(context);
mbutton1.settext("<<");
mbutton1.setgravity(gravity.left);
mbutton1.setonclicklistener(new onclicklistener()
{
@override
public void onclick(view view)
{
mgallery.moveprevious();
}
});
this.addview(mbutton1, new linearlayout.layoutparams(
linearlayout.layoutparams.match_parent,
linearlayout.layoutparams.wrap_content));
mbutton2 = new button(context);
mbutton2.settext(">>");
mbutton2.setgravity(gravity.right);
mbutton2.setonclicklistener(new onclicklistener()
{
@override
public void onclick(view view)
{
mgallery.movenext();
}
});
this.addview(mbutton2, new linearlayout.layoutparams(
linearlayout.layoutparams.match_parent,
linearlayout.layoutparams.wrap_content));
mtext2 = new textview(context);
mtext2.settext(mlabelarray[position]);
mtext2.settextsize(30);
mtext2.setgravity(gravity.right);
mtext2.setbackgroundcolor(mcolorarray[position]);
this.addview(mtext2, new linearlayout.layoutparams(
linearlayout.layoutparams.match_parent,
linearlayout.layoutparams.match_parent, 1));
}
}
}
[代码]flinggallery
复制代码 代码如下:
import android.content.context;
import android.view.gesturedetector;
import android.view.keyevent;
import android.view.motionevent;
import android.view.view;
import android.view.animation.animation;
import android.view.animation.animationutils;
import android.view.animation.interpolator;
import android.view.animation.transformation;
import android.widget.adapter;
import android.widget.framelayout;
import android.widget.linearlayout;
// todo:
// 1. in order to improve performance cache screen bitmap and use for animation
// 2. establish superfluous memory allocations and delay or replace with reused objects
// probably need to make sure we are not allocating objects (strings, etc.) in loops
public class flinggallery extends framelayout
{
// constants
private final int swipe_min_distance = 120;
private final int swipe_max_off_path = 250;
private final int swipe_threshold_veloicty = 400;
// properties
private int mviewpaddingwidth = 0;
private int manimationduration = 250;
private float msnapborderratio = 0.5f;
private boolean misgallerycircular = true;
// members
private int mgallerywidth = 0;
private boolean mistouched = false;
private boolean misdragging = false;
private float mcurrentoffset = 0.0f;
private long mscrolltimestamp = 0;
private int mflingdirection = 0;
private int mcurrentposition = 0;
private int mcurrentviewnumber = 0;
private context mcontext;
private adapter madapter;
private flinggalleryview[] mviews;
private flinggalleryanimation manimation;
private gesturedetector mgesturedetector;
private interpolator mdecelerateinterpolater;
public flinggallery(context context)
{
super(context);
mcontext = context;
madapter = null;
mviews = new flinggalleryview[3];
mviews[0] = new flinggalleryview(0, this);
mviews[1] = new flinggalleryview(1, this);
mviews[2] = new flinggalleryview(2, this);
manimation = new flinggalleryanimation();
mgesturedetector = new gesturedetector(new flinggesturedetector());
mdecelerateinterpolater = animationutils.loadinterpolator(mcontext, android.r.anim.decelerate_interpolator);
}
public void setpaddingwidth(int viewpaddingwidth)
{
mviewpaddingwidth = viewpaddingwidth;
}
public void setanimationduration(int animationduration)
{
manimationduration = animationduration;
}
public void setsnapborderratio(float snapborderratio)
{
msnapborderratio = snapborderratio;
}
public void setisgallerycircular(boolean isgallerycircular)
{
if (misgallerycircular != isgallerycircular)
{
misgallerycircular = isgallerycircular;
if (mcurrentposition == getfirstposition())
{
// we need to reload the view immediately to the left to change it to circular view or blank
mviews[getprevviewnumber(mcurrentviewnumber)].recycleview(getprevposition(mcurrentposition));
}
if (mcurrentposition == getlastposition())
{
// we need to reload the view immediately to the right to change it to circular view or blank
mviews[getnextviewnumber(mcurrentviewnumber)].recycleview(getnextposition(mcurrentposition));
}
}
}
public int getgallerycount()
{
return (madapter == null) ? 0 : madapter.getcount();
}
public int getfirstposition()
{
return 0;
}
public int getlastposition()
{
return (getgallerycount() == 0) ? 0 : getgallerycount() - 1;
}
private int getprevposition(int relativeposition)
{
int prevposition = relativeposition - 1;
if (prevposition < getfirstposition())
{
prevposition = getfirstposition() - 1;
if (misgallerycircular == true)
{
prevposition = getlastposition();
}
}
return prevposition;
}
private int getnextposition(int relativeposition)
{
int nextposition = relativeposition + 1;
if (nextposition > getlastposition())
{
nextposition = getlastposition() + 1;
if (misgallerycircular == true)
{
nextposition = getfirstposition();
}
}
return nextposition;
}
private int getprevviewnumber(int relativeviewnumber)
{
return (relativeviewnumber == 0) ? 2 : relativeviewnumber - 1;
}
private int getnextviewnumber(int relativeviewnumber)
{
return (relativeviewnumber == 2) ? 0 : relativeviewnumber + 1;
}
@override
protected void onlayout(boolean changed, int left, int top, int right, int bottom)
{
super.onlayout(changed, left, top, right, bottom);
// calculate our view width
mgallerywidth = right - left;
if (changed == true)
{
// position views at correct starting offsets
mviews[0].setoffset(0, 0, mcurrentviewnumber);
mviews[1].setoffset(0, 0, mcurrentviewnumber);
mviews[2].setoffset(0, 0, mcurrentviewnumber);
}
}
public void setadapter(adapter adapter)
{
madapter = adapter;
mcurrentposition = 0;
mcurrentviewnumber = 0;
// load the initial views from adapter
mviews[0].recycleview(mcurrentposition);
mviews[1].recycleview(getnextposition(mcurrentposition));
mviews[2].recycleview(getprevposition(mcurrentposition));
// position views at correct starting offsets
mviews[0].setoffset(0, 0, mcurrentviewnumber);
mviews[1].setoffset(0, 0, mcurrentviewnumber);
mviews[2].setoffset(0, 0, mcurrentviewnumber);
}
private int getviewoffset(int viewnumber, int relativeviewnumber)
{
// determine width including configured padding width
int offsetwidth = mgallerywidth + mviewpaddingwidth;
// position the previous view one measured width to left
if (viewnumber == getprevviewnumber(relativeviewnumber))
{
return offsetwidth;
}
// position the next view one measured width to the right
if (viewnumber == getnextviewnumber(relativeviewnumber))
{
return offsetwidth * -1;
}
return 0;
}
void moveprevious()
{
// slide to previous view
mflingdirection = 1;
processgesture();
}
void movenext()
{
// slide to next view
mflingdirection = -1;
processgesture();
}
@override
public boolean onkeydown(int keycode, keyevent event)
{
switch (keycode)
{
case keyevent.keycode_dpad_left:
moveprevious();
return true;
case keyevent.keycode_dpad_right:
movenext();
return true;
case keyevent.keycode_dpad_center:
case keyevent.keycode_enter:
}
return super.onkeydown(keycode, event);
}
public boolean ongallerytouchevent(motionevent event)
{
boolean consumed = mgesturedetector.ontouchevent(event);
if (event.getaction() == motionevent.action_up)
{
if (mistouched || misdragging)
{
processscrollsnap();
processgesture();
}
}
return consumed;
}
void processgesture()
{
int newviewnumber = mcurrentviewnumber;
int reloadviewnumber = 0;
int reloadposition = 0;
mistouched = false;
misdragging = false;
if (mflingdirection > 0)
{
if (mcurrentposition > getfirstposition() || misgallerycircular == true)
{
// determine previous view and outgoing view to recycle
newviewnumber = getprevviewnumber(mcurrentviewnumber);
mcurrentposition = getprevposition(mcurrentposition);
reloadviewnumber = getnextviewnumber(mcurrentviewnumber);
reloadposition = getprevposition(mcurrentposition);
}
}
if (mflingdirection < 0)
{
if (mcurrentposition < getlastposition() || misgallerycircular == true)
{
// determine the next view and outgoing view to recycle
newviewnumber = getnextviewnumber(mcurrentviewnumber);
mcurrentposition = getnextposition(mcurrentposition);
reloadviewnumber = getprevviewnumber(mcurrentviewnumber);
reloadposition = getnextposition(mcurrentposition);
}
}
if (newviewnumber != mcurrentviewnumber)
{
mcurrentviewnumber = newviewnumber;
// reload outgoing view from adapter in new position
mviews[reloadviewnumber].recycleview(reloadposition);
}
// ensure input focus on the current view
mviews[mcurrentviewnumber].requestfocus();
// run the slide animations for view transitions
manimation.prepareanimation(mcurrentviewnumber);
this.startanimation(manimation);
// reset fling state
mflingdirection = 0;
}
void processscrollsnap()
{
// snap to next view if scrolled passed snap position
float rolledgewidth = mgallerywidth * msnapborderratio;
int rolloffset = mgallerywidth - (int) rolledgewidth;
int currentoffset = mviews[mcurrentviewnumber].getcurrentoffset();
if (currentoffset <= rolloffset * -1)
{
// snap to previous view
mflingdirection = 1;
}
if (currentoffset >= rolloffset)
{
// snap to next view
mflingdirection = -1;
}
}
private class flinggalleryview
{
private int mviewnumber;
private framelayout mparentlayout;
private framelayout minvalidlayout = null;
private linearlayout minternallayout = null;
private view mexternalview = null;
public flinggalleryview(int viewnumber, framelayout parentlayout)
{
mviewnumber = viewnumber;
mparentlayout = parentlayout;
// invalid layout is used when outside gallery
minvalidlayout = new framelayout(mcontext);
minvalidlayout.setlayoutparams(new linearlayout.layoutparams(
layoutparams.match_parent, layoutparams.match_parent));
// internal layout is permanent for duration
minternallayout = new linearlayout(mcontext);
minternallayout.setlayoutparams(new linearlayout.layoutparams(
layoutparams.match_parent, layoutparams.match_parent));
mparentlayout.addview(minternallayout);
}
public void recycleview(int newposition)
{
if (mexternalview != null)
{
minternallayout.removeview(mexternalview);
}
if (madapter != null)
{
if (newposition >= getfirstposition() && newposition <= getlastposition())
{
mexternalview = madapter.getview(newposition, mexternalview, minternallayout);
}
else
{
mexternalview = minvalidlayout;
}
}
if (mexternalview != null)
{
minternallayout.addview(mexternalview, new linearlayout.layoutparams(
layoutparams.match_parent, layoutparams.match_parent));
}
}
public void setoffset(int xoffset, int yoffset, int relativeviewnumber)
{
// scroll the target view relative to its own position relative to currently displayed view
minternallayout.scrollto(getviewoffset(mviewnumber, relativeviewnumber) + xoffset, yoffset);
}
public int getcurrentoffset()
{
// return the current scroll position
return minternallayout.getscrollx();
}
public void requestfocus()
{
minternallayout.requestfocus();
}
}
private class flinggalleryanimation extends animation
{
private boolean misanimationinprogres;
private int mrelativeviewnumber;
private int minitialoffset;
private int mtargetoffset;
private int mtargetdistance;
public flinggalleryanimation()
{
misanimationinprogres = false;
mrelativeviewnumber = 0;
minitialoffset = 0;
mtargetoffset = 0;
mtargetdistance = 0;
}
public void prepareanimation(int relativeviewnumber)
{
// if we are animating relative to a new view
if (mrelativeviewnumber != relativeviewnumber)
{
if (misanimationinprogres == true)
{
// we only have three views so if requested again to animate in same direction we must snap
int newdirection = (relativeviewnumber == getprevviewnumber(mrelativeviewnumber)) ? 1 : -1;
int animdirection = (mtargetdistance < 0) ? 1 : -1;
// if animation in same direction
if (animdirection == newdirection)
{
// ran out of time to animate so snap to the target offset
mviews[0].setoffset(mtargetoffset, 0, mrelativeviewnumber);
mviews[1].setoffset(mtargetoffset, 0, mrelativeviewnumber);
mviews[2].setoffset(mtargetoffset, 0, mrelativeviewnumber);
}
}
// set relative view number for animation
mrelativeviewnumber = relativeviewnumber;
}
// note: in this implementation the targetoffset will always be zero
// as we are centering the view; but we include the calculations of
// targetoffset and targetdistance for use in future implementations
minitialoffset = mviews[mrelativeviewnumber].getcurrentoffset();
mtargetoffset = getviewoffset(mrelativeviewnumber, mrelativeviewnumber);
mtargetdistance = mtargetoffset - minitialoffset;
// configure base animation properties
this.setduration(manimationduration);
this.setinterpolator(mdecelerateinterpolater);
// start/continued animation
misanimationinprogres = true;
}
@override
protected void applytransformation(float interpolatedtime, transformation transformation)
{
// ensure interpolatedtime does not over-shoot then calculate new offset
interpolatedtime = (interpolatedtime > 1.0f) ? 1.0f : interpolatedtime;
int offset = minitialoffset + (int) (mtargetdistance * interpolatedtime);
for (int viewnumber = 0; viewnumber < 3; viewnumber++)
{
// only need to animate the visible views as the other view will always be off-screen
if ((mtargetdistance > 0 && viewnumber != getnextviewnumber(mrelativeviewnumber)) ||
(mtargetdistance < 0 && viewnumber != getprevviewnumber(mrelativeviewnumber)))
{
mviews[viewnumber].setoffset(offset, 0, mrelativeviewnumber);
}
}
}
@override
public boolean gettransformation(long currenttime, transformation outtransformation)
{
if (super.gettransformation(currenttime, outtransformation) == false)
{
// perform final adjustment to offsets to cleanup animation
mviews[0].setoffset(mtargetoffset, 0, mrelativeviewnumber);
mviews[1].setoffset(mtargetoffset, 0, mrelativeviewnumber);
mviews[2].setoffset(mtargetoffset, 0, mrelativeviewnumber);
// reached the animation target
misanimationinprogres = false;
return false;
}
// cancel if the screen touched
if (mistouched || misdragging)
{
// note that at this point we still consider ourselves to be animating
// because we have not yet reached the target offset; its just that the
// user has temporarily interrupted the animation with a touch gesture
return false;
}
return true;
}
}
private class flinggesturedetector extends gesturedetector.simpleongesturelistener
{
@override
public boolean ondown(motionevent e)
{
// stop animation
mistouched = true;
// reset fling state
mflingdirection = 0;
return true;
}
@override
public boolean onscroll(motionevent e1, motionevent e2, float distancex, float distancey)
{
if (e2.getaction() == motionevent.action_move)
{
if (misdragging == false)
{
// stop animation
mistouched = true;
// reconfigure scroll
misdragging = true;
mflingdirection = 0;
mscrolltimestamp = system.currenttimemillis();
mcurrentoffset = mviews[mcurrentviewnumber].getcurrentoffset();
}
float maxvelocity = mgallerywidth / (manimationduration / 1000.0f);
long timestampdelta = system.currenttimemillis() - mscrolltimestamp;
float maxscrolldelta = maxvelocity * (timestampdelta / 1000.0f);
float currentscrolldelta = e1.getx() - e2.getx();
if (currentscrolldelta < maxscrolldelta * -1) currentscrolldelta = maxscrolldelta * -1;
if (currentscrolldelta > maxscrolldelta) currentscrolldelta = maxscrolldelta;
int scrolloffset = math.round(mcurrentoffset + currentscrolldelta);
// we can't scroll more than the width of our own frame layout
if (scrolloffset >= mgallerywidth) scrolloffset = mgallerywidth;
if (scrolloffset <= mgallerywidth * -1) scrolloffset = mgallerywidth * -1;
mviews[0].setoffset(scrolloffset, 0, mcurrentviewnumber);
mviews[1].setoffset(scrolloffset, 0, mcurrentviewnumber);
mviews[2].setoffset(scrolloffset, 0, mcurrentviewnumber);
}
return false;
}
@override
public boolean onfling(motionevent e1, motionevent e2, float velocityx, float velocityy)
{
if (math.abs(e1.gety() - e2.gety()) <= swipe_max_off_path)
{
if (e2.getx() - e1.getx() > swipe_min_distance && math.abs(velocityx) > swipe_threshold_veloicty)
{
moveprevious();
}
if(e1.getx() - e2.getx() > swipe_min_distance && math.abs(velocityx) > swipe_threshold_veloicty)
{
movenext();
}
}
return false;
}
@override
public void onlongpress(motionevent e)
{
// finalise scrolling
mflingdirection = 0;
processgesture();
}
@override
public void onshowpress(motionevent e)
{
}
@override
public boolean onsingletapup(motionevent e)
{
// reset fling state
mflingdirection = 0;
return false;
}
}
}