I have a Gifview library which plays the animated gif. It's working fine in half screen but I need the gif to play in fullscreen mode. I Want to modify the onMeasure so that it can fit the fullscreen.
How can I change this method so that fullscreen is take in scale for each device?
public class GifView extends View {
private static final int DEFAULT_MOVIE_VIEW_DURATION = 1000;
private int mMovieResourceId;
private Movie movie;
private long mMovieStart;
private int mCurrentAnimationTime;
/**
* Position for drawing animation frames in the center of the view.
*/
private float mLeft;
private float mTop;
/**
* Scaling factor to fit the animation within view bounds.
*/
private float mScale;
/**
* Scaled movie frames width and height.
*/
private int mMeasuredMovieWidth;
private int mMeasuredMovieHeight;
private volatile boolean mPaused;
private boolean mVisible = true;
public GifView(Context context) {
this(context, null);
}
public GifView(Context context, AttributeSet attrs) {
this(context, attrs, R.styleable.CustomTheme_gifViewStyle);
}
public GifView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setViewAttributes(context, attrs, defStyle);
}
#SuppressLint("NewApi")
private void setViewAttributes(Context context, AttributeSet attrs, int defStyle) {
/**
* Starting from HONEYCOMB(Api Level:11) have to turn off HW acceleration to draw
* Movie on Canvas.
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
final TypedArray array = context.obtainStyledAttributes(attrs,
R.styleable.GifView, defStyle, R.style.Widget_GifView);
//-1 is default value
mMovieResourceId = array.getResourceId(R.styleable.GifView_gif, -1);
mPaused = array.getBoolean(R.styleable.GifView_paused, false);
array.recycle();
if (mMovieResourceId != -1) {
movie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId));
}
}
public void setGifResource(int movieResourceId) {
this.mMovieResourceId = movieResourceId;
movie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId));
requestLayout();
}
public int getGifResource() {
return this.mMovieResourceId;
}
public void play() {
if (this.mPaused) {
this.mPaused = false;
/**
* Calculate new movie start time, so that it resumes from the same
* frame.
*/
mMovieStart = android.os.SystemClock.uptimeMillis() - mCurrentAnimationTime;
invalidate();
}
}
//skip
public void next(){
if (this.mPaused) {
this.mPaused = false;
mMovieStart = mCurrentAnimationTime + movie.duration();
invalidate();
}
}
public void pause() {
if (!this.mPaused) {
this.mPaused = true;
invalidate();
}
}
public boolean isPaused() {
return this.mPaused;
}
public boolean isPlaying() {
return !this.mPaused;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (movie != null) {
int movieWidth = movie.width();
int movieHeight = movie.height();
/*
* Calculate horizontal scaling
*/
float scaleH = 1f;
int measureModeWidth = MeasureSpec.getMode(widthMeasureSpec);
if (measureModeWidth != MeasureSpec.UNSPECIFIED) {
int maximumWidth = MeasureSpec.getSize(widthMeasureSpec);
if (movieWidth > maximumWidth) {
scaleH = (float) movieWidth / (float) maximumWidth;
}
}
/*
* calculate vertical scaling
*/
float scaleW = 1f;
int measureModeHeight = MeasureSpec.getMode(heightMeasureSpec);
if (measureModeHeight != MeasureSpec.UNSPECIFIED) {
int maximumHeight = MeasureSpec.getSize(heightMeasureSpec);
if (movieHeight > maximumHeight) {
scaleW = (float) movieHeight / (float) maximumHeight;
}
}
/*
* calculate overall scale
*/
mScale = 1f / Math.max(scaleH, scaleW);
mMeasuredMovieWidth = (int) (movieWidth * mScale);
mMeasuredMovieHeight = (int) (movieHeight * mScale);
setMeasuredDimension(mMeasuredMovieWidth, mMeasuredMovieHeight);
} else {
/*
* No movie set, just set minimum available size.
*/
setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
}
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
/*
* Calculate mLeft / mTop for drawing in center
*/
mLeft = (getWidth() - mMeasuredMovieWidth) / 2f;
mTop = (getHeight() - mMeasuredMovieHeight) / 2f;
mVisible = getVisibility() == View.VISIBLE;
}
#Override
protected void onDraw(Canvas canvas) {
if (movie != null) {
if (!mPaused) {
updateAnimationTime();
drawMovieFrame(canvas);
invalidateView();
} else {
drawMovieFrame(canvas);
}
}
}
/**
* Invalidates view only if it is mVisible.
* <br>
* {#link #postInvalidateOnAnimation()} is used for Jelly Bean and higher.
*/
#SuppressLint("NewApi")
private void invalidateView() {
if (mVisible) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
postInvalidateOnAnimation();
} else {
invalidate();
}
}
}
/**
* Calculate current animation time
*/
private void updateAnimationTime() {
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) {
mMovieStart = now;
}
int dur = movie.duration();
if (dur == 0) {
dur = DEFAULT_MOVIE_VIEW_DURATION;
}
mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);
// Log.d(TAG, "updateAnimationTime: " + mCurrentAnimationTime);
}
/**
* Draw current GIF frame
*/
private void drawMovieFrame(Canvas canvas) {
movie.setTime(mCurrentAnimationTime);
// canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScale, mScale);
movie.draw(canvas, mLeft / mScale, mTop / mScale);
canvas.restore();
}
#SuppressLint("NewApi")
#Override
public void onScreenStateChanged(int screenState) {
super.onScreenStateChanged(screenState);
mVisible = screenState == SCREEN_STATE_ON;
invalidateView();
}
#SuppressLint("NewApi")
#Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
mVisible = visibility == View.VISIBLE;
invalidateView();
}
#Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == View.VISIBLE;
invalidateView();
}
}
xml layout
<com.world.example.GifView
android:id="#+id/gif1"
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:gif="#mipmap/tiktok" />
I want the gif image to play in fullscreen mode.
Try android:scaleType="fitXY" in xml layout
<com.world.example.GifView
android:id="#+id/mygif"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitXY"/>
Hope it helps !
Related
The image what i have got without " textureView.setRotation(90); "
https://i.stack.imgur.com/NK2VT.jpg
So now i need to rotate TextureView 90 degrees and Fit into same desired size.But if i use " textureView.setRotation(90); "
The output :
https://i.stack.imgur.com/IImW9.jpg
In this You can see the preview is not fit perfectly i.e above and below view is compressed ,so how to do so
The code :
private void startCamera() {
int index = getCameraId();
camera = Camera.open(index);
try {
Camera.Parameters parameters = camera.getParameters();
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes != null
&& focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
List<Camera.Size> cameraSizes = parameters.getSupportedPreviewSizes();
Size[] sizes = new Size[cameraSizes.size()];
int i = 0;
for (Camera.Size size : cameraSizes) {
sizes[i++] = new Size(size.width, size.height);
}
Size previewSize =
CameraConnectionFragment.chooseOptimalSize(
sizes, desiredSize.getWidth(), desiredSize.getHeight());
parameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight());
camera.setParameters(parameters);
camera.setPreviewTexture(availableSurfaceTexture);
} catch (IOException exception) {
camera.release();
}
camera.setPreviewCallbackWithBuffer(imageListener);
Camera.Size s = camera.getParameters().getPreviewSize();
camera.addCallbackBuffer(new byte[ImageUtils.getYUVByteSize(s.height, s.width)]);
textureView.setAspectRatio(s.height, s.width);
textureView.setRotation(90);
camera.startPreview();
}
AutoFitTextureView:
public class AutoFitTextureView extends TextureView {
private int ratioWidth = 0;
private int ratioHeight = 0;
public AutoFitTextureView(final Context context) {
this(context, null);
}
public AutoFitTextureView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoFitTextureView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
}
/**
* Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
* calculated from the parameters. Note that the actual sizes of parameters don't matter, that is,
* calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
*
* #param width Relative horizontal size
* #param height Relative vertical size
*/
public void setAspectRatio(final int width, final int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Size cannot be negative.");
}
ratioWidth = width;
ratioHeight = height;
requestLayout();
}
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == ratioWidth || 0 == ratioHeight) {
setMeasuredDimension(width, height);
} else {
if (width < height * ratioWidth / ratioHeight) {
setMeasuredDimension(width, width * ratioHeight / ratioWidth);
} else {
setMeasuredDimension(height * ratioWidth / ratioHeight, height);
}
}
}
}
Following this question
RecyclerView ItemTouchHelper Buttons on Swipe
(NOT THE SAME). I would like to add buttons on each side.
The official answer from that link is for ONE side only.
What I have tried:
public abstract class SwipeHelper extends ItemTouchHelper.SimpleCallback {
public static final int BUTTON_WIDTH = 125;
private RecyclerView recyclerView;
private List<UnderlayButton> buttons;
private GestureDetector gestureDetector;
private GestureDetector gestureDetector2;
private int swipedPos = -1;
private float swipeThreshold = 0.5f;
private Map<Integer, List<UnderlayButton>> buttonsBuffer;
private Queue<Integer> recoverQueue;
private GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener(){
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
for (UnderlayButton button : buttons){
if(button.onClick(e.getX(), e.getY()))
break;
}
return true;
}
};
private GestureDetector.SimpleOnGestureListener gestureListener2 = new GestureDetector.SimpleOnGestureListener(){
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
for (UnderlayButton button : buttons){
if(button.onClick2(e.getX(), e.getY()))
break;
}
return true;
}
};
private View.OnTouchListener onTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent e) {
if (swipedPos < 0) return false;
Point point = new Point((int) e.getRawX(), (int) e.getRawY());
RecyclerView.ViewHolder swipedViewHolder = recyclerView.findViewHolderForAdapterPosition(swipedPos);
View swipedItem = swipedViewHolder.itemView;
Rect rect = new Rect();
swipedItem.getGlobalVisibleRect(rect);
if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_UP ||e.getAction() == MotionEvent.ACTION_MOVE) {
if (rect.top < point.y && rect.bottom > point.y) {
gestureDetector.onTouchEvent(e);
gestureDetector2.onTouchEvent(e);
}
//else if(rect.top > point.y && rect.bottom < point.y){ }
else {
recoverQueue.add(swipedPos);
swipedPos = -1;
recoverSwipedItem();
}
}
return false;
}
};
public SwipeHelper(Context context, RecyclerView recyclerView) {
super(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
this.recyclerView = recyclerView;
this.buttons = new ArrayList<>();
this.gestureDetector = new GestureDetector(context, gestureListener);
this.gestureDetector2 = new GestureDetector(context, gestureListener2);
this.recyclerView.setOnTouchListener(onTouchListener);
buttonsBuffer = new HashMap<>();
recoverQueue = new LinkedList<Integer>(){
#Override
public boolean add(Integer o) {
if (contains(o))
return false;
else
return super.add(o);
}
};
attachSwipe();
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int pos = viewHolder.getAdapterPosition();
if (swipedPos != pos)
recoverQueue.add(swipedPos);
swipedPos = pos;
if (buttonsBuffer.containsKey(swipedPos))
buttons = buttonsBuffer.get(swipedPos);
else
buttons.clear();
buttonsBuffer.clear();
swipeThreshold = 0.5f * buttons.size() * BUTTON_WIDTH;
recoverSwipedItem();
}
#Override
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
return swipeThreshold;
}
#Override
public float getSwipeEscapeVelocity(float defaultValue) {
return 0.1f * defaultValue;
}
#Override
public float getSwipeVelocityThreshold(float defaultValue) {
return 5.0f * defaultValue;
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
int pos = viewHolder.getAdapterPosition();
float translationX = dX;
View itemView = viewHolder.itemView;
if (pos < 0){
swipedPos = pos;
return;
}
if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){
if(dX < 0) {
List<UnderlayButton> buffer = new ArrayList<>();
if (!buttonsBuffer.containsKey(pos)){
instantiateUnderlayButton(viewHolder, buffer);
buttonsBuffer.put(pos, buffer);
}
else {
buffer = buttonsBuffer.get(pos);
}
translationX = dX * buffer.size() * BUTTON_WIDTH / itemView.getWidth();
drawButtons(c, itemView, buffer, pos, translationX);
}
if(dX > 0) {
List<UnderlayButton> buffer = new ArrayList<>();
if (!buttonsBuffer.containsKey(pos)){
instantiateUnderlayButton(viewHolder, buffer);
buttonsBuffer.put(pos, buffer);
}
else {
buffer = buttonsBuffer.get(pos);
}
translationX = dX * buffer.size() * BUTTON_WIDTH / itemView.getWidth();
drawButtons(c, itemView, buffer, pos, translationX);
}
}
super.onChildDraw(c, recyclerView, viewHolder, translationX, dY, actionState, isCurrentlyActive);
}
private synchronized void recoverSwipedItem(){
while (!recoverQueue.isEmpty()){
int pos = recoverQueue.poll();
if (pos > -1) {
recyclerView.getAdapter().notifyItemChanged(pos);
}
}
}
private void drawButtons(Canvas c, View itemView, List<UnderlayButton> buffer, int pos, float dX) {
float right = itemView.getRight();
float left = itemView.getLeft();
float dButtonWidth = (-1) * dX / buffer.size();
for (UnderlayButton button : buffer) {
if (dX < 0) {
left = right - dButtonWidth;
button.onDraw(
c,
new RectF(
left,
itemView.getTop(),
right,
itemView.getBottom()
),
pos, dX
);
right = left;
} else if (dX > 0) {
right = left - dButtonWidth;
button.onDraw(c,
new RectF(
right,
itemView.getTop(),
left,
itemView.getBottom()
), pos, dX
);
left = right;
}
}
}
public void attachSwipe(){
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(this);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
public abstract void instantiateUnderlayButton(RecyclerView.ViewHolder viewHolder, List<UnderlayButton> underlayButtons);
public static class UnderlayButton {
private String text;
private int imageResId;
private int color;
private UnderlayButtonClickListener clickListener;
private String text2;
private int imageResId2;
private int color2;
private UnderlayButtonClickListener2 clickListener2;
private RectF clickRegion;
private RectF clickRegion2;
private int pos;
public UnderlayButton(String text, int imageResId, int color, UnderlayButtonClickListener clickListener, String text2, int imageResId2, int color2, UnderlayButtonClickListener2 clickListener2) {
this.text = text;
this.imageResId = imageResId;
this.color = color;
this.clickListener = clickListener;
this.text2 = text2;
this.imageResId2 = imageResId2;
this.color2 = color2;
this.clickListener2 = clickListener2;
}
public boolean onClick(float x, float y){
if (clickRegion != null && clickRegion.contains(x, y)){
clickListener.onClick(pos);
return true;
}
return false;
}
public boolean onClick2(float x, float y){
if (clickRegion2 != null && clickRegion2.contains(x, y)){
clickListener2.onClick(pos);
return true;
}
return false;
}
public void onDraw(Canvas c, RectF rect, int pos, float dX) {
Paint p = new Paint();
// Draw background
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (dX < 0) {
//Boton derecho
c.drawRect(rect, p);
p.setColor(color);
}
else{
//Boton Izquierdo
p.setColor(color2);
c.drawRect(rect, p);
}
clickRegion = rect;
clickRegion2 = rect;
this.pos = pos;
}
}
}
public interface UnderlayButtonClickListener {
void onClick(int pos);
}
public interface UnderlayButtonClickListener2 {
void onClick(int pos);
}}
I tried adding more parameters to the "UnderlayButton" class because before I tried to use the two classes separately but it was throwing me problems with the OnClickListener.
So the best way I found was to add extra parameters in the "UnderlayButton" class. Still, it doesn't work for me.
Could someone help me please.
I'm a beginner android developer and trying to create a Custom Pin Edittext using the following code and i want to set the color of the pin to transparent if filled,unfortunately there is no state_filled in the attr of android, how should i do that? Help me please here is my code.
public class PinEntryEditText extends AppCompatEditText {
public static final String XML_NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android";
private float mSpace = 24; //24 dp by default, space between the lines
private float mCharSize;
private float mNumChars = 6;
private float mLineSpacing = 8; //8dp by default, height of the text from our lines
private int mMaxLength = 6;
private OnClickListener mClickListener;
private float mLineStroke = 1; //1dp by default
private float mLineStrokeSelected = 2; //2dp by default
private Paint mLinesPaint;
int[][] mStates = new int[][]{
new int[]{android.R.attr.state_selected}, // selected
new int[]{android.R.attr.state_focused}, // focused
new int[]{-android.R.attr.state_focused}, // unfocused
};
//Green color = 0xFFB6C800
//Gray color = 0xFFCCCCCC
int[] mColors = new int[]{
0xFFB6C800,
0xFFCCCCCC,
0xFF880000,
};
ColorStateList mColorStates = new ColorStateList(mStates, mColors);
public PinEntryEditText(Context context) {
super(context);
}
public PinEntryEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public PinEntryEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
float multi = context.getResources().getDisplayMetrics().density;
mLineStroke = multi * mLineStroke;
mLineStrokeSelected = multi * mLineStrokeSelected;
mLinesPaint = new Paint(getPaint());
mLinesPaint.setStrokeWidth(mLineStroke);
setBackgroundResource(0);
mSpace = multi * mSpace; //convert to pixels for our density
mLineSpacing = multi * mLineSpacing; //convert to pixels for our density
mMaxLength = attrs.getAttributeIntValue(XML_NAMESPACE_ANDROID, "maxLength", 4);
mNumChars = mMaxLength;
//Disable copy paste
super.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
public void onDestroyActionMode(ActionMode mode) {
}
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
});
// When tapped, move cursor to end of text.
super.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
setSelection(getText().length());
if (mClickListener != null) {
mClickListener.onClick(v);
}
}
});
}
#Override
public void setOnClickListener(OnClickListener l) {
mClickListener = l;
}
#Override
public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
throw new RuntimeException("setCustomSelectionActionModeCallback() not supported.");
}
#Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);
int availableWidth = getWidth() - getPaddingRight() - getPaddingLeft();
if (mSpace < 0) {
mCharSize = (availableWidth / (mNumChars * 2 - 1));
} else {
mCharSize = (availableWidth - (mSpace * (mNumChars - 1))) / mNumChars;
}
int startX = getPaddingLeft();
int bottom = getHeight() - getPaddingBottom();
//Text Width
Editable text = getText();
int textLength = text.length();
float[] textWidths = new float[textLength];
getPaint().getTextWidths(getText(), 0, textLength, textWidths);
for (int i = 0; i < mNumChars; i++) {
updateColorForLines(i == textLength);
canvas.drawLine(startX, bottom, startX + mCharSize, bottom, mLinesPaint);
if (getText().length() > i) {
float middle = startX + mCharSize / 2;
canvas.drawText(text, i, i + 1, middle - textWidths[0] / 2, bottom - mLineSpacing, getPaint());
}
if (mSpace < 0) {
startX += mCharSize * 2;
} else {
startX += mCharSize + mSpace;
}
}
//mLinesPaint.setColor(getColorForState(0xff1a1f71));
}
private int getColorForState(int... states) {
return mColorStates.getColorForState(states, Color.GRAY);
}
/**
* #param next Is the current char the next character to be input?
*/
private void updateColorForLines(boolean next) {
if (isFocused()) {
mLinesPaint.setStrokeWidth(mLineStrokeSelected);
mLinesPaint.setColor(getColorForState(android.R.attr.state_focused));
if (next) {
mLinesPaint.setColor(getColorForState(android.R.attr.state_selected));
}
} else {
mLinesPaint.setStrokeWidth(mLineStroke);
mLinesPaint.setColor(getColorForState(-android.R.attr.state_focused));
}
}
}
Is there a way to do it?
there is no state_filled but you can create one. you can use TextWatcher interface it will be triggered whenever text will be updated. you can check there if current text size is equal to your mMaxLength set your pin color to transparent or else.
here is an example of TextWatcher implementation :
et1.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
I need to implement text line to show running right-to-left text news. I used this solution - https://stackoverflow.com/a/8971920, and added something like a messages stack and repeat times functionality. As result, there are java and xml parts of my project (link to Gist: https://gist.github.com/artseld/4aac2a3b75bb5a741216):
ScrollTextView.java
public class ScrollTextView extends TextView {
// scrolling feature
private Scroller mSlr;
// scroll speed
private float mScrollSpeed = 200f;
// the X offset when paused
private int mXPaused = 0;
// whether it's being paused
private boolean mPaused = true;
// messages
private LinkedList<Message> mMessages;
// current message
private String mMessage;
private int mRepeatTimes = 0;
/*
* constructor
*/
public ScrollTextView(Context context) {
this(context, null);
// customize the TextView
setSingleLine();
setEllipsize(null);
setVisibility(INVISIBLE);
}
/*
* constructor
*/
public ScrollTextView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
// customize the TextView
setSingleLine();
setEllipsize(null);
setVisibility(INVISIBLE);
}
/*
* constructor
*/
public ScrollTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// customize the TextView
setSingleLine();
setEllipsize(null);
setVisibility(INVISIBLE);
}
/**
* add message to messages pool
* #param message String
* #param repeatTimes int
*/
public void addMessage(String message, int repeatTimes) {
if (mMessages == null) {
mMessages = new LinkedList<>();
}
mMessages.add(new Message(message, repeatTimes));
if (mMessage == null) {
mMessage = message;
mRepeatTimes = repeatTimes;
}
startScroll();
}
public void addMessage(String message) {
addMessage(message, 1);
}
/**
* begin to scroll the text from the original position
*/
public void startScroll() {
if (mMessage == null)
return;
// set text
if (getText() == null || !getText().equals(mMessage)) {
setVisibility(INVISIBLE);
setText(mMessage);
}
// begin from the very right side
mXPaused = -1 * getWidth();
// assume it's paused
mPaused = true;
resumeScroll();
}
/**
* resume the scroll from the pausing point
*/
public void resumeScroll() {
if (!mPaused)
return;
// Do not know why it would not scroll sometimes
// if setHorizontallyScrolling is called in constructor.
setHorizontallyScrolling(true);
// use LinearInterpolator for steady scrolling
mSlr = new Scroller(this.getContext(), new LinearInterpolator());
setScroller(mSlr);
int scrollingLen = calculateScrollingLen();
int distance = scrollingLen - (getWidth() + mXPaused);
int duration = (int) (1000f * distance / mScrollSpeed);
setVisibility(VISIBLE);
mSlr.startScroll(mXPaused, 0, distance, 0, duration);
invalidate();
mPaused = false;
}
/**
* calculate the scrolling length of the text in pixel
*
* #return the scrolling length in pixels
*/
private int calculateScrollingLen() {
TextPaint tp = getPaint();
Rect rect = new Rect();
String strTxt = getText().toString();
tp.getTextBounds(strTxt, 0, strTxt.length(), rect);
int scrollingLen = rect.width() + getWidth();
rect = null;
return scrollingLen;
}
/**
* pause scrolling the text
*/
public void pauseScroll() {
if (null == mSlr)
return;
if (mPaused)
return;
mPaused = true;
// abortAnimation sets the current X to be the final X,
// and sets isFinished to be true
// so current position shall be saved
mXPaused = mSlr.getCurrX();
mSlr.abortAnimation();
}
#Override
/*
* override the computeScroll to restart scrolling when finished so as that
* the text is scrolled forever
*/
public void computeScroll() {
super.computeScroll();
if (null == mSlr) return;
if (mSlr.isFinished() && !mPaused) {
if (--mRepeatTimes > 0) {
startScroll();
return;
}
if (mMessages != null && !mMessages.isEmpty()) {
mMessage = mMessages.getFirst().getMessage();
mRepeatTimes = mMessages.getFirst().getRepeatTimes();
mMessages.removeFirst();
startScroll();
return;
}
mMessage = null;
mRepeatTimes = 0;
}
}
public float getScrollSpeed() {
return mScrollSpeed;
}
public void setScrollSpeed(float scrollSpeed) {
mScrollSpeed = scrollSpeed;
}
public boolean isPaused() {
return mPaused;
}
class Message {
private String mMessage;
private int mRepeatTimes = 1;
public Message(String message) {
mMessage = message;
}
public Message(String message, int repeatTimes) {
mMessage = message;
mRepeatTimes = repeatTimes;
}
public String getMessage() {
return mMessage;
}
public int getRepeatTimes() {
return mRepeatTimes;
}
}
}
MyActivity.java
public class MyActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
// ...
final ScrollTextView scrollText = (ScrollTextView) findViewById(R.id.scroll_text);
scrollText.addMessage("Message #1");
scrollText.addMessage("Message #2");
scrollText.addMessage("Message #3");
// ...
}
}
my_activity_layout.xml
<com.mydomain.myproject.ScrollTextView
android:id="#+id/scroll_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true" />
Almost all works good, but text appears on the left side of textview for a few milliseconds and only after that it runs from right to left as expected.
Can anybody help me? Thanks alot!
I give u some link for hint:
https://github.com/twotoasters/jazzylistview
https://play.google.com/store/apps/details?id=com.twotoasters.jazzylistview.sample
I already implemented JazzylistView but I also want to implement JazzylistView with PullToRefresh ListView. Is this possible? Anyone know this answer , please help me?
I find this solution. Just copy paste below code in PullToRefreshListView .java file in the library.
package com.smartupdater.widget;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;
import android.view.animation.RotateAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.notecrate.R;
import com.smartupdater.utils.Constants;
public class PullToRefreshListView extends ListView {
private final JazzyHelper mHelper;
private static final float PULL_RESISTANCE = 1.7f;
private static final int BOUNCE_ANIMATION_DURATION = 700;
private static final int BOUNCE_ANIMATION_DELAY = 100;
private static final float BOUNCE_OVERSHOOT_TENSION = 1.4f;
private static final int ROTATE_ARROW_ANIMATION_DURATION = 250;
private static enum State {
PULL_TO_REFRESH, RELEASE_TO_REFRESH, REFRESHING
}
/**
* Interface to implement when you want to get notified of 'pull to refresh'
* events. Call setOnRefreshListener(..) to activate an OnRefreshListener.
*/
public interface OnRefreshListener {
/**
* Method to be called when a refresh is requested
*/
public void onRefresh();
}
private static int measuredHeaderHeight;
private boolean scrollbarEnabled;
private boolean bounceBackHeader;
private boolean lockScrollWhileRefreshing;
private boolean showLastUpdatedText;
private String pullToRefreshText;
private String releaseToRefreshText;
private String refreshingText;
private String lastUpdatedText;
private SimpleDateFormat lastUpdatedDateFormat = new SimpleDateFormat("dd/MM HH:mm");
private float previousY;
private int headerPadding;
private boolean hasResetHeader;
private long lastUpdated = -1;
private State state;
private LinearLayout headerContainer;
private RelativeLayout header;
private RotateAnimation flipAnimation;
private RotateAnimation reverseFlipAnimation;
private ImageView image;
private ProgressBar spinner;
private TextView text;
private TextView lastUpdatedTextView;
private OnItemClickListener onItemClickListener;
private OnItemLongClickListener onItemLongClickListener;
private OnRefreshListener onRefreshListener;
public PullToRefreshListView(Context context) {
super(context);
mHelper = init(context, null);
init();
}
public PullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
mHelper = init(context, attrs);
init();
}
public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mHelper = init(context, attrs);
init();
}
private JazzyHelper init(Context context, AttributeSet attrs) {
JazzyHelper helper = new JazzyHelper(context, attrs);
super.setOnScrollListener(helper);
return helper;
}
#Override
public final void setOnScrollListener(OnScrollListener l) {
mHelper.setOnScrollListener(l);
}
public void setTransitionEffect(int transitionEffect) {
mHelper.setTransitionEffect(transitionEffect);
}
public void setShouldOnlyAnimateNewItems(boolean onlyAnimateNew) {
mHelper.setShouldOnlyAnimateNewItems(onlyAnimateNew);
}
public void setShouldOnlyAnimateFling(boolean onlyFling) {
mHelper.setShouldOnlyAnimateFling(onlyFling);
}
public void setSimulateGridWithList(boolean simulateGridWithList) {
mHelper.setSimulateGridWithList(simulateGridWithList);
setClipChildren(!simulateGridWithList);
}
public void setMaxAnimationVelocity(int itemsPerSecond) {
mHelper.setMaxAnimationVelocity(itemsPerSecond);
}
#Override
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
#Override
public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) {
this.onItemLongClickListener = onItemLongClickListener;
}
/**
* Activate an OnRefreshListener to get notified on 'pull to refresh'
* events.
*
* #param onRefreshListener
* The OnRefreshListener to get notified
*/
public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
this.onRefreshListener = onRefreshListener;
}
/**
* #return If the list is in 'Refreshing' state
*/
public boolean isRefreshing() {
return state == State.REFRESHING;
}
/**
* Default is false. When lockScrollWhileRefreshing is set to true, the list
* cannot scroll when in 'refreshing' mode. It's 'locked' on refreshing.
*
* #param lockScrollWhileRefreshing
*/
public void setLockScrollWhileRefreshing(boolean lockScrollWhileRefreshing) {
this.lockScrollWhileRefreshing = lockScrollWhileRefreshing;
}
/**
* Default is false. Show the last-updated date/time in the 'Pull ro
* Refresh' header. See 'setLastUpdatedDateFormat' to set the date/time
* formatting.
*
* #param showLastUpdatedText
*/
public void setShowLastUpdatedText(boolean showLastUpdatedText) {
this.showLastUpdatedText = showLastUpdatedText;
if (!showLastUpdatedText)
lastUpdatedTextView.setVisibility(View.GONE);
}
/**
* Default: "dd/MM HH:mm". Set the format in which the last-updated
* date/time is shown. Meaningless if 'showLastUpdatedText == false
* (default)'. See 'setShowLastUpdatedText'.
*
* #param lastUpdatedDateFormat
*/
public void setLastUpdatedDateFormat(SimpleDateFormat lastUpdatedDateFormat) {
this.lastUpdatedDateFormat = lastUpdatedDateFormat;
}
/**
* Explicitly set the state to refreshing. This is useful when you want to
* show the spinner and 'Refreshing' text when the refresh was not triggered
* by 'pull to refresh', for example on start.
*/
public void setRefreshing() {
state = State.REFRESHING;
scrollTo(0, 0);
setUiRefreshing();
setHeaderPadding(0);
}
/**
* Set the state back to 'pull to refresh'. Call this method when refreshing
* the data is finished.
*/
public void onRefreshComplete() {
state = State.PULL_TO_REFRESH;
resetHeader();
lastUpdated = System.currentTimeMillis();
}
/**
* Change the label text on state 'Pull to Refresh'
*
* #param pullToRefreshText
* Text
*/
public void setTextPullToRefresh(String pullToRefreshText) {
this.pullToRefreshText = pullToRefreshText;
if (state == State.PULL_TO_REFRESH) {
text.setText(pullToRefreshText);
}
}
/**
* Change the label text on state 'Release to Refresh'
*
* #param releaseToRefreshText
* Text
*/
public void setTextReleaseToRefresh(String releaseToRefreshText) {
this.releaseToRefreshText = releaseToRefreshText;
if (state == State.RELEASE_TO_REFRESH) {
text.setText(releaseToRefreshText);
}
}
/**
* Change the label text on state 'Refreshing'
*
* #param refreshingText
* Text
*/
public void setTextRefreshing(String refreshingText) {
this.refreshingText = refreshingText;
if (state == State.REFRESHING) {
text.setText(refreshingText);
}
}
private void init() {
setVerticalFadingEdgeEnabled(false);
headerContainer = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.layout_pulldown_header, null);
header = (RelativeLayout) headerContainer.findViewById(R.id.ptr_id_header);
text = (TextView) header.findViewById(R.id.ptr_id_text);
lastUpdatedTextView = (TextView) header.findViewById(R.id.ptr_id_last_updated);
image = (ImageView) header.findViewById(R.id.ptr_id_image);
spinner = (ProgressBar) header.findViewById(R.id.ptr_id_spinner);
pullToRefreshText = getContext().getString(R.string.ptr_pull_to_refresh);
releaseToRefreshText = getContext().getString(R.string.ptr_release_to_refresh);
refreshingText = getContext().getString(R.string.ptr_refreshing);
lastUpdatedText = getContext().getString(R.string.ptr_last_updated);
flipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
flipAnimation.setInterpolator(new LinearInterpolator());
flipAnimation.setDuration(ROTATE_ARROW_ANIMATION_DURATION);
flipAnimation.setFillAfter(true);
reverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
reverseFlipAnimation.setInterpolator(new LinearInterpolator());
reverseFlipAnimation.setDuration(ROTATE_ARROW_ANIMATION_DURATION);
reverseFlipAnimation.setFillAfter(true);
addHeaderView(headerContainer);
setState(State.PULL_TO_REFRESH);
scrollbarEnabled = isVerticalScrollBarEnabled();
ViewTreeObserver vto = header.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new PTROnGlobalLayoutListener());
super.setOnItemClickListener(new PTROnItemClickListener());
super.setOnItemLongClickListener(new PTROnItemLongClickListener());
}
private void setHeaderPadding(int padding) {
headerPadding = padding;
// Log.d(Constants.TAG, "Header Container=" + headerContainer +
// " Header=" + header + " Header Padding=" + headerPadding);
if (header != null) {
MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) header.getLayoutParams();
mlp.setMargins(0, Math.round(padding), 0, 0);
header.setLayoutParams(mlp);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (lockScrollWhileRefreshing && (state == State.REFRESHING || getAnimation() != null && !getAnimation().hasEnded())) {
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (getFirstVisiblePosition() == 0)
previousY = event.getY();
else
previousY = -1;
break;
case MotionEvent.ACTION_UP:
if (previousY != -1 && (state == State.RELEASE_TO_REFRESH || getFirstVisiblePosition() == 0)) {
switch (state) {
case RELEASE_TO_REFRESH:
setState(State.REFRESHING);
bounceBackHeader();
break;
case PULL_TO_REFRESH:
resetHeader();
break;
}
}
break;
case MotionEvent.ACTION_MOVE:
if (previousY != -1) {
float y = event.getY();
float diff = y - previousY;
if (diff > 0)
diff /= PULL_RESISTANCE;
previousY = y;
int newHeaderPadding = Math.max(Math.round(headerPadding + diff), -header.getHeight());
if (newHeaderPadding != headerPadding && state != State.REFRESHING) {
setHeaderPadding(newHeaderPadding);
if (state == State.PULL_TO_REFRESH && headerPadding > 0) {
setState(State.RELEASE_TO_REFRESH);
image.clearAnimation();
image.startAnimation(flipAnimation);
} else if (state == State.RELEASE_TO_REFRESH && headerPadding < 0) {
setState(State.PULL_TO_REFRESH);
image.clearAnimation();
image.startAnimation(reverseFlipAnimation);
}
return true;
}
}
break;
}
return super.onTouchEvent(event);
}
private void bounceBackHeader() {
int yTranslate = state == State.REFRESHING ? header.getHeight() - headerContainer.getHeight() : -headerContainer.getHeight()
- headerContainer.getTop();
TranslateAnimation bounceAnimation = new TranslateAnimation(TranslateAnimation.ABSOLUTE, 0, TranslateAnimation.ABSOLUTE, 0,
TranslateAnimation.ABSOLUTE, 0, TranslateAnimation.ABSOLUTE, yTranslate);
bounceAnimation.setDuration(BOUNCE_ANIMATION_DURATION);
bounceAnimation.setFillEnabled(true);
bounceAnimation.setFillAfter(false);
bounceAnimation.setFillBefore(true);
bounceAnimation.setInterpolator(new OvershootInterpolator(BOUNCE_OVERSHOOT_TENSION));
bounceAnimation.setAnimationListener(new HeaderAnimationListener(yTranslate));
startAnimation(bounceAnimation);
}
private void resetHeader() {
if (getFirstVisiblePosition() > 0) {
setHeaderPadding(-header.getHeight());
setState(State.PULL_TO_REFRESH);
return;
}
if (getAnimation() != null && !getAnimation().hasEnded()) {
bounceBackHeader = true;
} else {
bounceBackHeader();
}
}
private void setUiRefreshing() {
spinner.setVisibility(View.VISIBLE);
image.clearAnimation();
image.setVisibility(View.INVISIBLE);
text.setText(refreshingText);
}
private void setState(State state) {
this.state = state;
switch (state) {
case PULL_TO_REFRESH:
spinner.setVisibility(View.INVISIBLE);
image.setVisibility(View.VISIBLE);
text.setText(pullToRefreshText);
if (showLastUpdatedText && lastUpdated != -1) {
lastUpdatedTextView.setVisibility(View.VISIBLE);
lastUpdatedTextView.setText(String.format(lastUpdatedText, lastUpdatedDateFormat.format(new Date(lastUpdated))));
}
break;
case RELEASE_TO_REFRESH:
spinner.setVisibility(View.INVISIBLE);
image.setVisibility(View.VISIBLE);
text.setText(releaseToRefreshText);
break;
case REFRESHING:
setUiRefreshing();
lastUpdated = System.currentTimeMillis();
if (onRefreshListener == null) {
setState(State.PULL_TO_REFRESH);
} else {
onRefreshListener.onRefresh();
}
break;
}
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (!hasResetHeader) {
if (measuredHeaderHeight > 0 && state != State.REFRESHING) {
setHeaderPadding(-measuredHeaderHeight);
}
hasResetHeader = true;
}
}
private class HeaderAnimationListener implements AnimationListener {
private int height, translation;
private State stateAtAnimationStart;
public HeaderAnimationListener(int translation) {
this.translation = translation;
}
#Override
public void onAnimationStart(Animation animation) {
stateAtAnimationStart = state;
android.view.ViewGroup.LayoutParams lp = getLayoutParams();
height = lp.height;
lp.height = getHeight() - translation;
setLayoutParams(lp);
if (scrollbarEnabled) {
setVerticalScrollBarEnabled(false);
}
}
#Override
public void onAnimationEnd(Animation animation) {
setHeaderPadding(stateAtAnimationStart == State.REFRESHING ? 0 : -measuredHeaderHeight - headerContainer.getTop());
setSelection(0);
android.view.ViewGroup.LayoutParams lp = getLayoutParams();
lp.height = height;
setLayoutParams(lp);
if (scrollbarEnabled) {
setVerticalScrollBarEnabled(true);
}
if (bounceBackHeader) {
bounceBackHeader = false;
postDelayed(new Runnable() {
#Override
public void run() {
resetHeader();
}
}, BOUNCE_ANIMATION_DELAY);
} else if (stateAtAnimationStart != State.REFRESHING) {
setState(State.PULL_TO_REFRESH);
}
}
#Override
public void onAnimationRepeat(Animation animation) {
}
}
private class PTROnGlobalLayoutListener implements OnGlobalLayoutListener {
#Override
public void onGlobalLayout() {
int initialHeaderHeight = header.getHeight();
if (initialHeaderHeight > 0) {
measuredHeaderHeight = initialHeaderHeight;
if (measuredHeaderHeight > 0 && state != State.REFRESHING) {
setHeaderPadding(-measuredHeaderHeight);
requestLayout();
}
}
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
private class PTROnItemClickListener implements OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
hasResetHeader = false;
if (onItemClickListener != null && state == State.PULL_TO_REFRESH) {
// Passing up onItemClick. Correct position with the number of
// header views
onItemClickListener.onItemClick(adapterView, view, position - getHeaderViewsCount(), id);
}
}
}
private class PTROnItemLongClickListener implements OnItemLongClickListener {
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position, long id) {
hasResetHeader = false;
if (onItemLongClickListener != null && state == State.PULL_TO_REFRESH) {
// Passing up onItemLongClick. Correct position with the number
// of header views
return onItemLongClickListener.onItemLongClick(adapterView, view, position - getHeaderViewsCount(), id);
}
return false;
}
}
}
In fact,you can combine the two frame by modify the source code.
first,modify PullToRefreshAdapterViewBase.java
remove the final,because you have override it in PullToRefreshListView.the purpose is hijack the OnScrollListener,pass it to JazzyHelper.
public void setOnScrollListener(OnScrollListener listener) {
mOnScrollListener = listener;
}
the code in PullToRefreshListView.java.
/*******************************************************************************
* Copyright 2011, 2012 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.handmark.pulltorefresh.library;
import com.example.jazzydemo.R;
import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor;
import com.handmark.pulltorefresh.library.internal.LoadingLayout;
import com.xp.lvbh.others.jazzylistview.JazzyEffect;
import com.xp.lvbh.others.jazzylistview.JazzyHelper;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.AbsListView.OnScrollListener;
public class PullToRefreshListView extends PullToRefreshAdapterViewBase<ListView> {
private final JazzyHelper mHelper;
private LoadingLayout mHeaderLoadingView;
private LoadingLayout mFooterLoadingView;
private FrameLayout mLvFooterLoadingFrame;
private boolean mListViewExtrasEnabled;
public PullToRefreshListView(Context context) {
super(context);
mHelper = init(context, null);
}
public PullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
mHelper = init(context, null);
}
public PullToRefreshListView(Context context, Mode mode) {
super(context, mode);
mHelper = init(context, null);
}
public PullToRefreshListView(Context context, Mode mode, AnimationStyle style) {
super(context, mode, style);
mHelper = init(context, null);
}
#Override
public final Orientation getPullToRefreshScrollDirection() {
return Orientation.VERTICAL;
}
#Override
protected void onRefreshing(final boolean doScroll) {
/**
* If we're not showing the Refreshing view, or the list is empty, the
* the header/footer views won't show so we use the normal method.
*/
ListAdapter adapter = mRefreshableView.getAdapter();
if (!mListViewExtrasEnabled || !getShowViewWhileRefreshing() || null == adapter || adapter.isEmpty()) {
super.onRefreshing(doScroll);
return;
}
super.onRefreshing(false);
final LoadingLayout origLoadingView, listViewLoadingView, oppositeListViewLoadingView;
final int selection, scrollToY;
switch (getCurrentMode()) {
case MANUAL_REFRESH_ONLY:
case PULL_FROM_END:
origLoadingView = getFooterLayout();
listViewLoadingView = mFooterLoadingView;
oppositeListViewLoadingView = mHeaderLoadingView;
selection = mRefreshableView.getCount() - 1;
scrollToY = getScrollY() - getFooterSize();
break;
case PULL_FROM_START:
default:
origLoadingView = getHeaderLayout();
listViewLoadingView = mHeaderLoadingView;
oppositeListViewLoadingView = mFooterLoadingView;
selection = 0;
scrollToY = getScrollY() + getHeaderSize();
break;
}
// Hide our original Loading View
origLoadingView.reset();
origLoadingView.hideAllViews();
// Make sure the opposite end is hidden too
oppositeListViewLoadingView.setVisibility(View.GONE);
// Show the ListView Loading View and set it to refresh.
listViewLoadingView.setVisibility(View.VISIBLE);
listViewLoadingView.refreshing();
if (doScroll) {
// We need to disable the automatic visibility changes for now
disableLoadingLayoutVisibilityChanges();
// We scroll slightly so that the ListView's header/footer is at the
// same Y position as our normal header/footer
setHeaderScroll(scrollToY);
// Make sure the ListView is scrolled to show the loading
// header/footer
mRefreshableView.setSelection(selection);
// Smooth scroll as normal
smoothScrollTo(0);
}
}
#Override
protected void onReset() {
/**
* If the extras are not enabled, just call up to super and return.
*/
if (!mListViewExtrasEnabled) {
super.onReset();
return;
}
final LoadingLayout originalLoadingLayout, listViewLoadingLayout;
final int scrollToHeight, selection;
final boolean scrollLvToEdge;
switch (getCurrentMode()) {
case MANUAL_REFRESH_ONLY:
case PULL_FROM_END:
originalLoadingLayout = getFooterLayout();
listViewLoadingLayout = mFooterLoadingView;
selection = mRefreshableView.getCount() - 1;
scrollToHeight = getFooterSize();
scrollLvToEdge = Math.abs(mRefreshableView.getLastVisiblePosition() - selection) <= 1;
break;
case PULL_FROM_START:
default:
originalLoadingLayout = getHeaderLayout();
listViewLoadingLayout = mHeaderLoadingView;
scrollToHeight = -getHeaderSize();
selection = 0;
scrollLvToEdge = Math.abs(mRefreshableView.getFirstVisiblePosition() - selection) <= 1;
break;
}
// If the ListView header loading layout is showing, then we need to
// flip so that the original one is showing instead
if (listViewLoadingLayout.getVisibility() == View.VISIBLE) {
// Set our Original View to Visible
originalLoadingLayout.showInvisibleViews();
// Hide the ListView Header/Footer
listViewLoadingLayout.setVisibility(View.GONE);
/**
* Scroll so the View is at the same Y as the ListView
* header/footer, but only scroll if: we've pulled to refresh, it's
* positioned correctly
*/
if (scrollLvToEdge && getState() != State.MANUAL_REFRESHING) {
mRefreshableView.setSelection(selection);
setHeaderScroll(scrollToHeight);
}
}
// Finally, call up to super
super.onReset();
}
#Override
protected LoadingLayoutProxy createLoadingLayoutProxy(final boolean includeStart, final boolean includeEnd) {
LoadingLayoutProxy proxy = super.createLoadingLayoutProxy(includeStart, includeEnd);
if (mListViewExtrasEnabled) {
final Mode mode = getMode();
if (includeStart && mode.showHeaderLoadingLayout()) {
proxy.addLayout(mHeaderLoadingView);
}
if (includeEnd && mode.showFooterLoadingLayout()) {
proxy.addLayout(mFooterLoadingView);
}
}
return proxy;
}
protected ListView createListView(Context context, AttributeSet attrs) {
final ListView lv;
if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
lv = new InternalListViewSDK9(context, attrs);
} else {
lv = new InternalListView(context, attrs);
}
return lv;
}
#Override
protected ListView createRefreshableView(Context context, AttributeSet attrs) {
ListView lv = createListView(context, attrs);
// Set it to this so it can be used in ListActivity/ListFragment
lv.setId(android.R.id.list);
return lv;
}
#Override
protected void handleStyledAttributes(TypedArray a) {
super.handleStyledAttributes(a);
mListViewExtrasEnabled = a.getBoolean(R.styleable.PullToRefresh_ptrListViewExtrasEnabled, true);
if (mListViewExtrasEnabled) {
final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL);
// Create Loading Views ready for use later
FrameLayout frame = new FrameLayout(getContext());
mHeaderLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_START, a);
mHeaderLoadingView.setVisibility(View.GONE);
frame.addView(mHeaderLoadingView, lp);
mRefreshableView.addHeaderView(frame, null, false);
mLvFooterLoadingFrame = new FrameLayout(getContext());
mFooterLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_END, a);
mFooterLoadingView.setVisibility(View.GONE);
mLvFooterLoadingFrame.addView(mFooterLoadingView, lp);
/**
* If the value for Scrolling While Refreshing hasn't been
* explicitly set via XML, enable Scrolling While Refreshing.
*/
if (!a.hasValue(R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled)) {
setScrollingWhileRefreshingEnabled(true);
}
}
}
#TargetApi(9)
final class InternalListViewSDK9 extends InternalListView {
public InternalListViewSDK9(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
// Does all of the hard work...
OverscrollHelper.overScrollBy(PullToRefreshListView.this, deltaX, scrollX, deltaY, scrollY, isTouchEvent);
return returnValue;
}
}
protected class InternalListView extends ListView implements EmptyViewMethodAccessor {
private boolean mAddedLvFooter = false;
public InternalListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void dispatchDraw(Canvas canvas) {
/**
* This is a bit hacky, but Samsung's ListView has got a bug in it
* when using Header/Footer Views and the list is empty. This masks
* the issue so that it doesn't cause an FC. See Issue #66.
*/
try {
super.dispatchDraw(canvas);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
/**
* This is a bit hacky, but Samsung's ListView has got a bug in it
* when using Header/Footer Views and the list is empty. This masks
* the issue so that it doesn't cause an FC. See Issue #66.
*/
try {
return super.dispatchTouchEvent(ev);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
return false;
}
}
#Override
public void setAdapter(ListAdapter adapter) {
// Add the Footer View at the last possible moment
if (null != mLvFooterLoadingFrame && !mAddedLvFooter) {
addFooterView(mLvFooterLoadingFrame, null, false);
mAddedLvFooter = true;
}
super.setAdapter(adapter);
}
#Override
public void setEmptyView(View emptyView) {
PullToRefreshListView.this.setEmptyView(emptyView);
}
#Override
public void setEmptyViewInternal(View emptyView) {
super.setEmptyView(emptyView);
}
}
//add jazzy listview
/**
* Sets the desired transition effect.
*
* #param transitionEffect The non-bundled transition provided by the client.
*/
public void setTransitionEffect(JazzyEffect transitionEffect) {
mHelper.setTransitionEffect(transitionEffect);
}
#Override
public void setOnScrollListener(OnScrollListener l) {
mHelper.setOnScrollListener(l);
}
private JazzyHelper init(Context context, AttributeSet attrs) {
JazzyHelper helper = new JazzyHelper(context, attrs);
super.setOnScrollListener(helper);
return helper;
}
}
I hava verify the code.wish can help you.
Here is a demo,you can try it :
https://github.com/XIONGDEYI/PullToRefreshListViewAndJazzyListView