How to create emoticons on android - android

Could someone help me understand or guide me on some reading material on how to create emoticons and how they work on android?
I need to know the whole process from a layman versus programmatical point of view.

We cannot create our own custom emoticons for generic keyboard in android. Because these images are stored in form of codes. which may not be implemented by facebook,skype and other. We have to follow the build in emoticons See list,
If you want to use them within your app. Use this
See this and this.
See SoftKeyboard sample here
And tutorial on Creating a Custom keyboard

See the article : https://www.androidhive.info/2016/11/android-integrate-emojis-keyboard-app/
to understand how emojis works and how to implement emoji feature for your application.
See the article : https://apps.timwhitlock.info/emoji/tables/unicode
to determine the unicode of emojis.
you can get input emoji from keyboard. resize it according to your requirement.for rotation and flipping support you can play with the layout, holding the emoji.

use custom library :
https://github.com/rockerhieu/emojicon
public class EmojiKeyboard {
private static final String TAG = "EmojiKeyboard";
private static final String PREF_KEY_HEIGHT_KB = "EmojiKbHeight";
private Context context;
private int screenHeight = -1;
private int emojiKbHeight = -1;
private PopupWindow emojiKeyboardPopup;
private View view;
private SharedPreferences preferences;
public EmojiKeyboard(Context context, View view) {
if (context instanceof Activity) {
this.context = context;
this.view = view;
preferences = context.getSharedPreferences(context.getString(R.string.app_name), Context.MODE_PRIVATE);
//Restore EmojiKeyboard Height
emojiKbHeight = preferences.getInt(PREF_KEY_HEIGHT_KB, -1);
//TODO support less then 11 API, and not perfect resizing when switched the keyboard
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
/*
* Get root view height
* */
screenHeight = screenHeight == -1 && bottom > oldBottom
? bottom
: screenHeight;
/*
* Calculate soft keyboard height
* */
int dHeight = oldBottom - bottom;
boolean validHeight = emojiKbHeight == -1 && dHeight > 80 && bottom != oldBottom;
/*
* Сheck twice because the keyboard may have been switched
* */
emojiKbHeight = validHeight
? dHeight : emojiKbHeight != (dHeight) && dHeight > 0
? dHeight
: emojiKbHeight;
/*
* Store emoji keyboard height into SharedPreferences
* */
preferences.edit().putInt(PREF_KEY_HEIGHT_KB, emojiKbHeight).commit();
/*
* If layout returned to a standard height then dismissing keyboard (OnBackPressed)
* */
if (screenHeight == bottom) {
dismissEmojiKeyboard();
}
/*
* Resize emoji on the go when a user switches between keyboards
* */
resizeEmoji();
}
});
}
}
public void showEmoji() {
if (emojiKeyboardPopup == null) {
createEmojiKeyboard();
}
if (!isShowed()) {
new Handler().postDelayed(new Runnable() {
public void run() {
emojiKeyboardPopup.showAtLocation(view, Gravity.BOTTOM, 0, 0);
resizeEmoji();
}
}, 10L);
} else {
dismissEmojiKeyboard();
}
}
public void createEmojiKeyboard() {
EmojiView emojiKeyboard = new EmojiView(context, EmojiView.EMOJI_DARK_STYLE, new EmojiView.onEmojiClickListener() {
public void onBackspace() {
if (((Activity) context).getWindow().getCurrentFocus() instanceof EditText) {
((Activity) context).getWindow().getCurrentFocus().dispatchKeyEvent(new KeyEvent(0, 67));
}
}
public void onEmojiSelected(Emojicon emojicon) {
if (((Activity) context).getWindow().getCurrentFocus() instanceof EditText) {
EmojiView.input((EditText) ((Activity) context).getWindow().getCurrentFocus(), emojicon);
}
}
});
emojiKeyboardPopup = new PopupWindow(emojiKeyboard);
emojiKeyboardPopup.setHeight(View.MeasureSpec.makeMeasureSpec(setEmojiKeyboardHeight(), View.MeasureSpec.EXACTLY));
emojiKeyboardPopup.setWidth(View.MeasureSpec.makeMeasureSpec(getDisplayDimensions(context).x, View.MeasureSpec.EXACTLY));
emojiKeyboardPopup.setAnimationStyle(0);
}
public void dismissEmojiKeyboard() {
if (isShowed()) {
emojiKeyboardPopup.dismiss();
}
}
public boolean isShowed() {
return emojiKeyboardPopup != null && emojiKeyboardPopup.isShowing();
}
/*
* Emoji set up size
* */
public void resizeEmoji() {
if (isShowed()) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) emojiKeyboardPopup.getContentView().getLayoutParams();
layoutParams.height = setEmojiKeyboardHeight();
wm.updateViewLayout(emojiKeyboardPopup.getContentView(), layoutParams);
}
}
public int setEmojiKeyboardHeight() {
return emojiKbHeight == -1 && emojiKbHeight != screenHeight && emojiKbHeight < 80
? (getDisplayDimensions(context).y / 2)
: emojiKbHeight;
}
public Point getDisplayDimensions(Context context) {
Point size = new Point();
WindowManager w = ((Activity) context).getWindowManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
w.getDefaultDisplay().getSize(size);
} else {
Display d = w.getDefaultDisplay();
size.x = d.getWidth();
size.y = d.getHeight();
}
return size;
}
}

use lib github https://github.com/rockerhieu/emojicon
public class EmojiKeyboard {
private static final String TAG = "EmojiKeyboard";
private static final String PREF_KEY_HEIGHT_KB = "EmojiKbHeight";
private Context context;
private int screenHeight = -1;
private int emojiKbHeight = -1;
private PopupWindow emojiKeyboardPopup;
private View view;
private SharedPreferences preferences;
public EmojiKeyboard(Context context, View view) {
if (context instanceof Activity) {
this.context = context;
this.view = view;
preferences = context.getSharedPreferences(context.getString(R.string.app_name), Context.MODE_PRIVATE);
//Restore EmojiKeyboard Height
emojiKbHeight = preferences.getInt(PREF_KEY_HEIGHT_KB, -1);
//TODO support less then 11 API, and not perfect resizing when switched the keyboard
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
/*
* Get root view height
* */
screenHeight = screenHeight == -1 && bottom > oldBottom
? bottom
: screenHeight;
/*
* Calculate soft keyboard height
* */
int dHeight = oldBottom - bottom;
boolean validHeight = emojiKbHeight == -1 && dHeight > 80 && bottom != oldBottom;
/*
* Сheck twice because the keyboard may have been switched
* */
emojiKbHeight = validHeight
? dHeight : emojiKbHeight != (dHeight) && dHeight > 0
? dHeight
: emojiKbHeight;
/*
* Store emoji keyboard height into SharedPreferences
* */
preferences.edit().putInt(PREF_KEY_HEIGHT_KB, emojiKbHeight).commit();
/*
* If layout returned to a standard height then dismissing keyboard (OnBackPressed)
* */
if (screenHeight == bottom) {
dismissEmojiKeyboard();
}
/*
* Resize emoji on the go when a user switches between keyboards
* */
resizeEmoji();
}
});
}
}
public void showEmoji() {
if (emojiKeyboardPopup == null) {
createEmojiKeyboard();
}
if (!isShowed()) {
new Handler().postDelayed(new Runnable() {
public void run() {
emojiKeyboardPopup.showAtLocation(view, Gravity.BOTTOM, 0, 0);
resizeEmoji();
}
}, 10L);
} else {
dismissEmojiKeyboard();
}
}
public void createEmojiKeyboard() {
EmojiView emojiKeyboard = new EmojiView(context, EmojiView.EMOJI_DARK_STYLE, new EmojiView.onEmojiClickListener() {
public void onBackspace() {
if (((Activity) context).getWindow().getCurrentFocus() instanceof EditText) {
((Activity) context).getWindow().getCurrentFocus().dispatchKeyEvent(new KeyEvent(0, 67));
}
}
public void onEmojiSelected(Emojicon emojicon) {
if (((Activity) context).getWindow().getCurrentFocus() instanceof EditText) {
EmojiView.input((EditText) ((Activity) context).getWindow().getCurrentFocus(), emojicon);
}
}
});
emojiKeyboardPopup = new PopupWindow(emojiKeyboard);
emojiKeyboardPopup.setHeight(View.MeasureSpec.makeMeasureSpec(setEmojiKeyboardHeight(), View.MeasureSpec.EXACTLY));
emojiKeyboardPopup.setWidth(View.MeasureSpec.makeMeasureSpec(getDisplayDimensions(context).x, View.MeasureSpec.EXACTLY));
emojiKeyboardPopup.setAnimationStyle(0);
}
public void dismissEmojiKeyboard() {
if (isShowed()) {
emojiKeyboardPopup.dismiss();
}
}
public boolean isShowed() {
return emojiKeyboardPopup != null && emojiKeyboardPopup.isShowing();
}
/*
* Emoji set up size
* */
public void resizeEmoji() {
if (isShowed()) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) emojiKeyboardPopup.getContentView().getLayoutParams();
layoutParams.height = setEmojiKeyboardHeight();
wm.updateViewLayout(emojiKeyboardPopup.getContentView(), layoutParams);
}
}
public int setEmojiKeyboardHeight() {
return emojiKbHeight == -1 && emojiKbHeight != screenHeight && emojiKbHeight < 80
? (getDisplayDimensions(context).y / 2)
: emojiKbHeight;
}
public Point getDisplayDimensions(Context context) {
Point size = new Point();
WindowManager w = ((Activity) context).getWindowManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
w.getDefaultDisplay().getSize(size);
} else {
Display d = w.getDefaultDisplay();
size.x = d.getWidth();
size.y = d.getHeight();
}
return size;
}
}

Related

Swipe refresh layout is not working for web view.

I am using swipe refresh layout but it is not working for web view. It is working for a simple activity layout, but when i am using a web view it's not working properly.
Here is my code.
public class SwipeActivity extends Activity implements OnRefreshListener{
SwipeRefreshLayout swipeLayout;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_swipe);
swipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);
swipeLayout.setOnRefreshListener(this);
swipeLayout.setColorScheme(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
}
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
#Override public void run() {
swipeLayout.setRefreshing(false);
}
}, 5000);
}
}
And this one is swipe refresh layout.
public class SwipeRefreshLayout extends ViewGroup {
private static final long RETURN_TO_ORIGINAL_POSITION_TIMEOUT = 300;
private static final float ACCELERATE_INTERPOLATION_FACTOR = 1.5f;
private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;
private static final float PROGRESS_BAR_HEIGHT = 4;
private static final float MAX_SWIPE_DISTANCE_FACTOR = .6f;
private static final int REFRESH_TRIGGER_DISTANCE = 120;
private SwipeProgressBar mProgressBar; //the thing that shows progress is going
private View mTarget; //the content that gets pulled down
private int mOriginalOffsetTop;
private OnRefreshListener mListener;
private MotionEvent mDownEvent;
private int mFrom;
private boolean mRefreshing = false;
private int mTouchSlop;
private float mDistanceToTriggerSync = -1;
private float mPrevY;
private int mMediumAnimationDuration;
private float mFromPercentage = 0;
private float mCurrPercentage = 0;
private int mProgressBarHeight;
private int mCurrentTargetOffsetTop;
// Target is returning to its start offset because it was cancelled or a
// refresh was triggered.
private boolean mReturningToStart;
private final DecelerateInterpolator mDecelerateInterpolator;
private final AccelerateInterpolator mAccelerateInterpolator;
private static final int[] LAYOUT_ATTRS = new int[] {
android.R.attr.enabled
};
private final Animation mAnimateToStartPosition = new Animation() {
#Override
public void applyTransformation(float interpolatedTime, Transformation t) {
int targetTop = 0;
if (mFrom != mOriginalOffsetTop) {
targetTop = (mFrom + (int)((mOriginalOffsetTop - mFrom) * interpolatedTime));
}
int offset = targetTop - mTarget.getTop();
final int currentTop = mTarget.getTop();
if (offset + currentTop < 0) {
offset = 0 - currentTop;
}
setTargetOffsetTopAndBottom(offset);
}
};
private Animation mShrinkTrigger = new Animation() {
#Override
public void applyTransformation(float interpolatedTime, Transformation t) {
float percent = mFromPercentage + ((0 - mFromPercentage) * interpolatedTime);
mProgressBar.setTriggerPercentage(percent);
}
};
private final AnimationListener mReturnToStartPositionListener = new BaseAnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
// Once the target content has returned to its start position, reset
// the target offset to 0
mCurrentTargetOffsetTop = 0;
}
};
private final AnimationListener mShrinkAnimationListener = new BaseAnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
mCurrPercentage = 0;
}
};
private final Runnable mReturnToStartPosition = new Runnable() {
#Override
public void run() {
mReturningToStart = true;
animateOffsetToStartPosition(mCurrentTargetOffsetTop + getPaddingTop(),
mReturnToStartPositionListener);
}
};
// Cancel the refresh gesture and animate everything back to its original state.
private final Runnable mCancel = new Runnable() {
#Override
public void run() {
mReturningToStart = true;
// Timeout fired since the user last moved their finger; animate the
// trigger to 0 and put the target back at its original position
if (mProgressBar != null) {
mFromPercentage = mCurrPercentage;
mShrinkTrigger.setDuration(mMediumAnimationDuration);
mShrinkTrigger.setAnimationListener(mShrinkAnimationListener);
mShrinkTrigger.reset();
mShrinkTrigger.setInterpolator(mDecelerateInterpolator);
startAnimation(mShrinkTrigger);
}
animateOffsetToStartPosition(mCurrentTargetOffsetTop + getPaddingTop(),
mReturnToStartPositionListener);
}
};
/**
* Simple constructor to use when creating a SwipeRefreshLayout from code.
* #param context
*/
public SwipeRefreshLayout(Context context) {
this(context, null);
}
/**
* Constructor that is called when inflating SwipeRefreshLayout from XML.
* #param context
* #param attrs
*/
public SwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mMediumAnimationDuration = getResources().getInteger(
android.R.integer.config_mediumAnimTime);
setWillNotDraw(false);
mProgressBar = new SwipeProgressBar(this);
final DisplayMetrics metrics = getResources().getDisplayMetrics();
mProgressBarHeight = (int) (metrics.density * PROGRESS_BAR_HEIGHT);
mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
mAccelerateInterpolator = new AccelerateInterpolator(ACCELERATE_INTERPOLATION_FACTOR);
final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
setEnabled(a.getBoolean(0, true));
a.recycle();
}
#Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
removeCallbacks(mCancel);
removeCallbacks(mReturnToStartPosition);
}
#Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
removeCallbacks(mReturnToStartPosition);
removeCallbacks(mCancel);
}
private void animateOffsetToStartPosition(int from, AnimationListener listener) {
mFrom = from;
mAnimateToStartPosition.reset();
mAnimateToStartPosition.setDuration(mMediumAnimationDuration);
mAnimateToStartPosition.setAnimationListener(listener);
mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator);
mTarget.startAnimation(mAnimateToStartPosition);
}
/**
* Set the listener to be notified when a refresh is triggered via the swipe
* gesture.
*/
public void setOnRefreshListener(SwipeActivity swipeActivity) {
mListener = swipeActivity;
}
private void setTriggerPercentage(float percent) {
if (percent == 0f) {
// No-op. A null trigger means it's uninitialized, and setting it to zero-percent
// means we're trying to reset state, so there's nothing to reset in this case.
mCurrPercentage = 0;
return;
}
mCurrPercentage = percent;
mProgressBar.setTriggerPercentage(percent);
}
/**
* Notify the widget that refresh state has changed. Do not call this when
* refresh is triggered by a swipe gesture.
*
* #param refreshing Whether or not the view should show refresh progress.
*/
public void setRefreshing(boolean refreshing) {
if (mRefreshing != refreshing) {
ensureTarget();
mCurrPercentage = 0;
mRefreshing = refreshing;
if (mRefreshing) {
mProgressBar.start();
} else {
mProgressBar.stop();
}
}
}
/**
* Set the four colors used in the progress animation. The first color will
* also be the color of the bar that grows in response to a user swipe
* gesture.
*
* #param colorRes1 Color resource.
* #param colorRes2 Color resource.
* #param colorRes3 Color resource.
* #param colorRes4 Color resource.
*/
public void setColorScheme(int colorRes1, int colorRes2, int colorRes3, int colorRes4) {
ensureTarget();
final Resources res = getResources();
final int color1 = res.getColor(colorRes1);
final int color2 = res.getColor(colorRes2);
final int color3 = res.getColor(colorRes3);
final int color4 = res.getColor(colorRes4);
mProgressBar.setColorScheme(color1, color2, color3,color4);
}
/**
* #return Whether the SwipeRefreshWidget is actively showing refresh
* progress.
*/
public boolean isRefreshing() {
return mRefreshing;
}
private void ensureTarget() {
// Don't bother getting the parent height if the parent hasn't been laid out yet.
if (mTarget == null) {
if (getChildCount() > 1 && !isInEditMode()) {
throw new IllegalStateException(
"SwipeRefreshLayout can host only one direct child");
}
mTarget = getChildAt(0);
mOriginalOffsetTop = mTarget.getTop() + getPaddingTop();
}
if (mDistanceToTriggerSync == -1) {
if (getParent() != null && ((View)getParent()).getHeight() > 0) {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
mDistanceToTriggerSync = (int) Math.min(
((View) getParent()) .getHeight() * MAX_SWIPE_DISTANCE_FACTOR,
REFRESH_TRIGGER_DISTANCE * metrics.density);
}
}
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
mProgressBar.draw(canvas);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int width = getMeasuredWidth();
final int height = getMeasuredHeight();
mProgressBar.setBounds(0, 0, width, mProgressBarHeight);
if (getChildCount() == 0) {
return;
}
final View child = getChildAt(0);
final int childLeft = getPaddingLeft();
final int childTop = mCurrentTargetOffsetTop + getPaddingTop();
final int childWidth = width - getPaddingLeft() - getPaddingRight();
final int childHeight = height - getPaddingTop() - getPaddingBottom();
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (getChildCount() > 1 && !isInEditMode()) {
throw new IllegalStateException("SwipeRefreshLayout can host only one direct child");
}
if (getChildCount() > 0) {
getChildAt(0).measure(
MeasureSpec.makeMeasureSpec(
getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(
getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
MeasureSpec.EXACTLY));
}
}
/**
* #return Whether it is possible for the child view of this layout to
* scroll up. Override this if the child view is a custom view.
*/
public boolean canChildScrollUp() {
if (android.os.Build.VERSION.SDK_INT < 14) {
if (mTarget instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTarget;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
.getTop() < absListView.getPaddingTop());
} else {
return mTarget.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mTarget, -1);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
ensureTarget();
boolean handled = false;
if (mReturningToStart && ev.getAction() == MotionEvent.ACTION_DOWN) {
mReturningToStart = false;
}
if (isEnabled() && !mReturningToStart && !canChildScrollUp()) {
handled = onTouchEvent(ev);
}
return !handled ? super.onInterceptTouchEvent(ev) : handled;
}
#Override
public void requestDisallowInterceptTouchEvent(boolean b) {
// Nope.
}
#Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
boolean handled = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
mCurrPercentage = 0;
mDownEvent = MotionEvent.obtain(event);
mPrevY = mDownEvent.getY();
break;
case MotionEvent.ACTION_MOVE:
if (mDownEvent != null && !mReturningToStart) {
final float eventY = event.getY();
float yDiff = eventY - mDownEvent.getY();
if (yDiff > mTouchSlop) {
// User velocity passed min velocity; trigger a refresh
if (yDiff > mDistanceToTriggerSync) {
// User movement passed distance; trigger a refresh
startRefresh();
handled = true;
break;
} else {
// Just track the user's movement
setTriggerPercentage(
mAccelerateInterpolator.getInterpolation(
yDiff / mDistanceToTriggerSync));
float offsetTop = yDiff;
if (mPrevY > eventY) {
offsetTop = yDiff - mTouchSlop;
}
updateContentOffsetTop((int) (offsetTop));
if (mPrevY > eventY && (mTarget.getTop() < mTouchSlop)) {
// If the user puts the view back at the top, we
// don't need to. This shouldn't be considered
// cancelling the gesture as the user can restart from the top.
removeCallbacks(mCancel);
} else {
updatePositionTimeout();
}
mPrevY = event.getY();
handled = true;
}
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mDownEvent != null) {
mDownEvent.recycle();
mDownEvent = null;
}
break;
}
return handled;
}
private void startRefresh() {
removeCallbacks(mCancel);
mReturnToStartPosition.run();
setRefreshing(true);
mListener.onRefresh();
}
private void updateContentOffsetTop(int targetTop) {
final int currentTop = mTarget.getTop();
if (targetTop > mDistanceToTriggerSync) {
targetTop = (int) mDistanceToTriggerSync;
} else if (targetTop < 0) {
targetTop = 0;
}
setTargetOffsetTopAndBottom(targetTop - currentTop);
}
private void setTargetOffsetTopAndBottom(int offset) {
mTarget.offsetTopAndBottom(offset);
mCurrentTargetOffsetTop = mTarget.getTop();
}
private void updatePositionTimeout() {
removeCallbacks(mCancel);
postDelayed(mCancel, RETURN_TO_ORIGINAL_POSITION_TIMEOUT);
}
/**
* Classes that wish to be notified when the swipe gesture correctly
* triggers a refresh should implement this interface.
*/
public interface OnRefreshListener {
public void onRefresh();
}
/**
* Simple AnimationListener to avoid having to implement unneeded methods in
* AnimationListeners.
*/
private class BaseAnimationListener implements AnimationListener {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
}
}

SwipeRefreshLayout not showing colors at first and only shows when .setRefreshing(false) called

I am making an android app that uses a SwipeRefreshLayout around a RelativeLayout in a fragment in a ViewPager. When I first open the app and swipe to refresh, the refresh disc shows, but it is blank with no colors.
http://i.stack.imgur.com/VzUeB.png
Then, the next time I swipe to refresh, there is no preview of the refresh (the disc does not come down), and when I release, it takes a second to show. I think it only runs when swipeRefreshLayout.setRefreshing(false) is called, and so it only shows for a split second.
This is my code:
swipeRefreshLayout = (SwipeRefreshLayout)view.findViewById(R.id.swipe_container);
swipeRefreshLayout.setColorSchemeResources(R.color.accentColor, R.color.colorPrimary, R.color.colorPrimaryDark);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
TypedValue typed_value = new TypedValue();
getActivity().getTheme().resolveAttribute(android.support.v7.appcompat.R.attr.actionBarSize, typed_value, true);
DisplayMetrics dm = getResources().getDisplayMetrics();
int height = dm.heightPixels;
swipeRefreshLayout.setProgressViewOffset(false, -200, height / 8);
swipeRefreshLayout.post(new Runnable() {
#Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
}
});
try {
result = new GetReadResultTask().execute().get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
if (result != null && connected) {
AlphaAnimation fadeOut = new AlphaAnimation(1.0f, 0.0f);
AlphaAnimation fadeIn = new AlphaAnimation(0.0f, 1.0f);
stepsTextView.setAnimation(fadeOut);
fadeOut.setDuration(1200);
fadeOut.setFillAfter(true);
List<Bucket> buckets = result.getBuckets();
for (int iii = 0; iii < buckets.size(); iii++) {
dumpDataSet(buckets.get(iii).getDataSet(DataType.AGGREGATE_STEP_COUNT_DELTA));
}
stepsTextView.setAnimation(fadeIn);
fadeIn.setDuration(1200);
fadeIn.setFillAfter(true);
series.resetData(data);
}
swipeRefreshLayout.post(new Runnable() {
#Override
public void run() {
swipeRefreshLayout.setRefreshing(false);
}
});
}
});
Use following code for this..
SwipeActivity Activity is as follows..
public class SwipeActivity extends Activity implements OnRefreshListener{
SwipeRefreshLayout swipeLayout;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_swipe);
swipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);
swipeLayout.setOnRefreshListener(this);
swipeLayout.setColorScheme(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
}
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
#Override public void run() {
swipeLayout.setRefreshing(false);
}
}, 5000);
}
}
SwipeRefreshLayout is follows.
public class SwipeRefreshLayout extends ViewGroup {
private static final long RETURN_TO_ORIGINAL_POSITION_TIMEOUT = 300;
private static final float ACCELERATE_INTERPOLATION_FACTOR = 1.5f;
private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;
private static final float PROGRESS_BAR_HEIGHT = 4;
private static final float MAX_SWIPE_DISTANCE_FACTOR = .6f;
private static final int REFRESH_TRIGGER_DISTANCE = 120;
private SwipeProgressBar mProgressBar; //the thing that shows progress is going
private View mTarget; //the content that gets pulled down
private int mOriginalOffsetTop;
private OnRefreshListener mListener;
private MotionEvent mDownEvent;
private int mFrom;
private boolean mRefreshing = false;
private int mTouchSlop;
private float mDistanceToTriggerSync = -1;
private float mPrevY;
private int mMediumAnimationDuration;
private float mFromPercentage = 0;
private float mCurrPercentage = 0;
private int mProgressBarHeight;
private int mCurrentTargetOffsetTop;
// Target is returning to its start offset because it was cancelled or a
// refresh was triggered.
private boolean mReturningToStart;
private final DecelerateInterpolator mDecelerateInterpolator;
private final AccelerateInterpolator mAccelerateInterpolator;
private static final int[] LAYOUT_ATTRS = new int[] {
android.R.attr.enabled
};
private final Animation mAnimateToStartPosition = new Animation() {
#Override
public void applyTransformation(float interpolatedTime, Transformation t) {
int targetTop = 0;
if (mFrom != mOriginalOffsetTop) {
targetTop = (mFrom + (int)((mOriginalOffsetTop - mFrom) * interpolatedTime));
}
int offset = targetTop - mTarget.getTop();
final int currentTop = mTarget.getTop();
if (offset + currentTop < 0) {
offset = 0 - currentTop;
}
setTargetOffsetTopAndBottom(offset);
}
};
private Animation mShrinkTrigger = new Animation() {
#Override
public void applyTransformation(float interpolatedTime, Transformation t) {
float percent = mFromPercentage + ((0 - mFromPercentage) * interpolatedTime);
mProgressBar.setTriggerPercentage(percent);
}
};
private final AnimationListener mReturnToStartPositionListener = new BaseAnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
// Once the target content has returned to its start position, reset
// the target offset to 0
mCurrentTargetOffsetTop = 0;
}
};
private final AnimationListener mShrinkAnimationListener = new BaseAnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
mCurrPercentage = 0;
}
};
private final Runnable mReturnToStartPosition = new Runnable() {
#Override
public void run() {
mReturningToStart = true;
animateOffsetToStartPosition(mCurrentTargetOffsetTop + getPaddingTop(),
mReturnToStartPositionListener);
}
};
// Cancel the refresh gesture and animate everything back to its original state.
private final Runnable mCancel = new Runnable() {
#Override
public void run() {
mReturningToStart = true;
// Timeout fired since the user last moved their finger; animate the
// trigger to 0 and put the target back at its original position
if (mProgressBar != null) {
mFromPercentage = mCurrPercentage;
mShrinkTrigger.setDuration(mMediumAnimationDuration);
mShrinkTrigger.setAnimationListener(mShrinkAnimationListener);
mShrinkTrigger.reset();
mShrinkTrigger.setInterpolator(mDecelerateInterpolator);
startAnimation(mShrinkTrigger);
}
animateOffsetToStartPosition(mCurrentTargetOffsetTop + getPaddingTop(),
mReturnToStartPositionListener);
}
};
/**
* Simple constructor to use when creating a SwipeRefreshLayout from code.
* #param context
*/
public SwipeRefreshLayout(Context context) {
this(context, null);
}
/**
* Constructor that is called when inflating SwipeRefreshLayout from XML.
* #param context
* #param attrs
*/
public SwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mMediumAnimationDuration = getResources().getInteger(
android.R.integer.config_mediumAnimTime);
setWillNotDraw(false);
mProgressBar = new SwipeProgressBar(this);
final DisplayMetrics metrics = getResources().getDisplayMetrics();
mProgressBarHeight = (int) (metrics.density * PROGRESS_BAR_HEIGHT);
mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
mAccelerateInterpolator = new AccelerateInterpolator(ACCELERATE_INTERPOLATION_FACTOR);
final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
setEnabled(a.getBoolean(0, true));
a.recycle();
}
#Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
removeCallbacks(mCancel);
removeCallbacks(mReturnToStartPosition);
}
#Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
removeCallbacks(mReturnToStartPosition);
removeCallbacks(mCancel);
}
private void animateOffsetToStartPosition(int from, AnimationListener listener) {
mFrom = from;
mAnimateToStartPosition.reset();
mAnimateToStartPosition.setDuration(mMediumAnimationDuration);
mAnimateToStartPosition.setAnimationListener(listener);
mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator);
mTarget.startAnimation(mAnimateToStartPosition);
}
/**
* Set the listener to be notified when a refresh is triggered via the swipe
* gesture.
*/
public void setOnRefreshListener(SwipeActivity swipeActivity) {
mListener = swipeActivity;
}
private void setTriggerPercentage(float percent) {
if (percent == 0f) {
// No-op. A null trigger means it's uninitialized, and setting it to zero-percent
// means we're trying to reset state, so there's nothing to reset in this case.
mCurrPercentage = 0;
return;
}
mCurrPercentage = percent;
mProgressBar.setTriggerPercentage(percent);
}
/**
* Notify the widget that refresh state has changed. Do not call this when
* refresh is triggered by a swipe gesture.
*
* #param refreshing Whether or not the view should show refresh progress.
*/
public void setRefreshing(boolean refreshing) {
if (mRefreshing != refreshing) {
ensureTarget();
mCurrPercentage = 0;
mRefreshing = refreshing;
if (mRefreshing) {
mProgressBar.start();
} else {
mProgressBar.stop();
}
}
}
/**
* Set the four colors used in the progress animation. The first color will
* also be the color of the bar that grows in response to a user swipe
* gesture.
*
* #param colorRes1 Color resource.
* #param colorRes2 Color resource.
* #param colorRes3 Color resource.
* #param colorRes4 Color resource.
*/
public void setColorScheme(int colorRes1, int colorRes2, int colorRes3, int colorRes4) {
ensureTarget();
final Resources res = getResources();
final int color1 = res.getColor(colorRes1);
final int color2 = res.getColor(colorRes2);
final int color3 = res.getColor(colorRes3);
final int color4 = res.getColor(colorRes4);
mProgressBar.setColorScheme(color1, color2, color3,color4);
}
/**
* #return Whether the SwipeRefreshWidget is actively showing refresh
* progress.
*/
public boolean isRefreshing() {
return mRefreshing;
}
private void ensureTarget() {
// Don't bother getting the parent height if the parent hasn't been laid out yet.
if (mTarget == null) {
if (getChildCount() > 1 && !isInEditMode()) {
throw new IllegalStateException(
"SwipeRefreshLayout can host only one direct child");
}
mTarget = getChildAt(0);
mOriginalOffsetTop = mTarget.getTop() + getPaddingTop();
}
if (mDistanceToTriggerSync == -1) {
if (getParent() != null && ((View)getParent()).getHeight() > 0) {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
mDistanceToTriggerSync = (int) Math.min(
((View) getParent()) .getHeight() * MAX_SWIPE_DISTANCE_FACTOR,
REFRESH_TRIGGER_DISTANCE * metrics.density);
}
}
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
mProgressBar.draw(canvas);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int width = getMeasuredWidth();
final int height = getMeasuredHeight();
mProgressBar.setBounds(0, 0, width, mProgressBarHeight);
if (getChildCount() == 0) {
return;
}
final View child = getChildAt(0);
final int childLeft = getPaddingLeft();
final int childTop = mCurrentTargetOffsetTop + getPaddingTop();
final int childWidth = width - getPaddingLeft() - getPaddingRight();
final int childHeight = height - getPaddingTop() - getPaddingBottom();
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (getChildCount() > 1 && !isInEditMode()) {
throw new IllegalStateException("SwipeRefreshLayout can host only one direct child");
}
if (getChildCount() > 0) {
getChildAt(0).measure(
MeasureSpec.makeMeasureSpec(
getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(
getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
MeasureSpec.EXACTLY));
}
}
/**
* #return Whether it is possible for the child view of this layout to
* scroll up. Override this if the child view is a custom view.
*/
public boolean canChildScrollUp() {
if (android.os.Build.VERSION.SDK_INT < 14) {
if (mTarget instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTarget;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
.getTop() < absListView.getPaddingTop());
} else {
return mTarget.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mTarget, -1);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
ensureTarget();
boolean handled = false;
if (mReturningToStart && ev.getAction() == MotionEvent.ACTION_DOWN) {
mReturningToStart = false;
}
if (isEnabled() && !mReturningToStart && !canChildScrollUp()) {
handled = onTouchEvent(ev);
}
return !handled ? super.onInterceptTouchEvent(ev) : handled;
}
#Override
public void requestDisallowInterceptTouchEvent(boolean b) {
// Nope.
}
#Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
boolean handled = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
mCurrPercentage = 0;
mDownEvent = MotionEvent.obtain(event);
mPrevY = mDownEvent.getY();
break;
case MotionEvent.ACTION_MOVE:
if (mDownEvent != null && !mReturningToStart) {
final float eventY = event.getY();
float yDiff = eventY - mDownEvent.getY();
if (yDiff > mTouchSlop) {
// User velocity passed min velocity; trigger a refresh
if (yDiff > mDistanceToTriggerSync) {
// User movement passed distance; trigger a refresh
startRefresh();
handled = true;
break;
} else {
// Just track the user's movement
setTriggerPercentage(
mAccelerateInterpolator.getInterpolation(
yDiff / mDistanceToTriggerSync));
float offsetTop = yDiff;
if (mPrevY > eventY) {
offsetTop = yDiff - mTouchSlop;
}
updateContentOffsetTop((int) (offsetTop));
if (mPrevY > eventY && (mTarget.getTop() < mTouchSlop)) {
// If the user puts the view back at the top, we
// don't need to. This shouldn't be considered
// cancelling the gesture as the user can restart from the top.
removeCallbacks(mCancel);
} else {
updatePositionTimeout();
}
mPrevY = event.getY();
handled = true;
}
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mDownEvent != null) {
mDownEvent.recycle();
mDownEvent = null;
}
break;
}
return handled;
}
private void startRefresh() {
removeCallbacks(mCancel);
mReturnToStartPosition.run();
setRefreshing(true);
mListener.onRefresh();
}
private void updateContentOffsetTop(int targetTop) {
final int currentTop = mTarget.getTop();
if (targetTop > mDistanceToTriggerSync) {
targetTop = (int) mDistanceToTriggerSync;
} else if (targetTop < 0) {
targetTop = 0;
}
setTargetOffsetTopAndBottom(targetTop - currentTop);
}
private void setTargetOffsetTopAndBottom(int offset) {
mTarget.offsetTopAndBottom(offset);
mCurrentTargetOffsetTop = mTarget.getTop();
}
private void updatePositionTimeout() {
removeCallbacks(mCancel);
postDelayed(mCancel, RETURN_TO_ORIGINAL_POSITION_TIMEOUT);
}
/**
* Classes that wish to be notified when the swipe gesture correctly
* triggers a refresh should implement this interface.
*/
public interface OnRefreshListener {
public void onRefresh();
}
/**
* Simple AnimationListener to avoid having to implement unneeded methods in
* AnimationListeners.
*/
private class BaseAnimationListener implements AnimationListener {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
}
}
following is manifest file..
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.swiperefreshlayout"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.swiperefreshlayout.SwipeActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Just copy paste all code in your project and you will get your desired output.

Android replace keyboard with Emoji Fragment

I'm making a little chat in my app and I have an Emoticon Fragment (just like the one from whatsapp or telegram). How can I switch between the fragment and the keyboard without any weird animation?
I already have the fragment wih the emojis and the custom EditText. I just want to switch beween that fragment and keyboard. I really want it to work like whatsapp or Telegram.
For the emoticon Fragment I made a library. I add a fragment (Grid view with SpannableTextViews for each emoji) in the same layout as the EditText.
Any help will be really appreciated.
You don't need to replace the keyboard, you can put your fragment over activity using PopupWindow as Telegram does. Just look at the source: method showEmojiPopup creates EmojiView and put it inside PopupWindow then calculates appropriate size and shows it.
emojiPopup.setHeight(View.MeasureSpec.makeMeasureSpec(currentHeight, View.MeasureSpec.EXACTLY));
emojiPopup.setWidth(View.MeasureSpec.makeMeasureSpec(contentView.getWidth(), View.MeasureSpec.EXACTLY));
emojiPopup.showAtLocation(parentActivity.getWindow().getDecorView(), 83, 0, 0);
You need to code :
public class EmojiKeyboard {
private static final String TAG = "EmojiKeyboard";
private static final String PREF_KEY_HEIGHT_KB = "EmojiKbHeight";
private Context context;
private int screenHeight = -1;
private int emojiKbHeight = -1;
private PopupWindow emojiKeyboardPopup;
private View view;
private SharedPreferences preferences;
public EmojiKeyboard(Context context, View view) {
if (context instanceof Activity) {
this.context = context;
this.view = view;
preferences = context.getSharedPreferences(context.getString(R.string.app_name), Context.MODE_PRIVATE);
//Restore EmojiKeyboard Height
emojiKbHeight = preferences.getInt(PREF_KEY_HEIGHT_KB, -1);
//TODO support less then 11 API, and not perfect resizing when switched the keyboard
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
/*
* Get root view height
* */
screenHeight = screenHeight == -1 && bottom > oldBottom
? bottom
: screenHeight;
/*
* Calculate soft keyboard height
* */
int dHeight = oldBottom - bottom;
boolean validHeight = emojiKbHeight == -1 && dHeight > 80 && bottom != oldBottom;
/*
* Сheck twice because the keyboard may have been switched
* */
emojiKbHeight = validHeight
? dHeight : emojiKbHeight != (dHeight) && dHeight > 0
? dHeight
: emojiKbHeight;
/*
* Store emoji keyboard height into SharedPreferences
* */
preferences.edit().putInt(PREF_KEY_HEIGHT_KB, emojiKbHeight).commit();
/*
* If layout returned to a standard height then dismissing keyboard (OnBackPressed)
* */
if (screenHeight == bottom) {
dismissEmojiKeyboard();
}
/*
* Resize emoji on the go when a user switches between keyboards
* */
resizeEmoji();
}
});
}
}
public void showEmoji() {
if (emojiKeyboardPopup == null) {
createEmojiKeyboard();
}
if (!isShowed()) {
new Handler().postDelayed(new Runnable() {
public void run() {
emojiKeyboardPopup.showAtLocation(view, Gravity.BOTTOM, 0, 0);
resizeEmoji();
}
}, 10L);
} else {
dismissEmojiKeyboard();
}
}
public void createEmojiKeyboard() {
EmojiView emojiKeyboard = new EmojiView(context, EmojiView.EMOJI_DARK_STYLE, new EmojiView.onEmojiClickListener() {
public void onBackspace() {
if (((Activity) context).getWindow().getCurrentFocus() instanceof EditText) {
((Activity) context).getWindow().getCurrentFocus().dispatchKeyEvent(new KeyEvent(0, 67));
}
}
public void onEmojiSelected(Emojicon emojicon) {
if (((Activity) context).getWindow().getCurrentFocus() instanceof EditText) {
EmojiView.input((EditText) ((Activity) context).getWindow().getCurrentFocus(), emojicon);
}
}
});
emojiKeyboardPopup = new PopupWindow(emojiKeyboard);
emojiKeyboardPopup.setHeight(View.MeasureSpec.makeMeasureSpec(setEmojiKeyboardHeight(), View.MeasureSpec.EXACTLY));
emojiKeyboardPopup.setWidth(View.MeasureSpec.makeMeasureSpec(getDisplayDimensions(context).x, View.MeasureSpec.EXACTLY));
emojiKeyboardPopup.setAnimationStyle(0);
}
public void dismissEmojiKeyboard() {
if (isShowed()) {
emojiKeyboardPopup.dismiss();
}
}
public boolean isShowed() {
return emojiKeyboardPopup != null && emojiKeyboardPopup.isShowing();
}
/*
* Emoji set up size
* */
public void resizeEmoji() {
if (isShowed()) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) emojiKeyboardPopup.getContentView().getLayoutParams();
layoutParams.height = setEmojiKeyboardHeight();
wm.updateViewLayout(emojiKeyboardPopup.getContentView(), layoutParams);
}
}
public int setEmojiKeyboardHeight() {
return emojiKbHeight == -1 && emojiKbHeight != screenHeight && emojiKbHeight < 80
? (getDisplayDimensions(context).y / 2)
: emojiKbHeight;
}
public Point getDisplayDimensions(Context context) {
Point size = new Point();
WindowManager w = ((Activity) context).getWindowManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
w.getDefaultDisplay().getSize(size);
} else {
Display d = w.getDefaultDisplay();
size.x = d.getWidth();
size.y = d.getHeight();
}
return size;
}
}
Solve problem

Android Spannabe Emoji loads slow in GridView and TextView

I am making a little chat in my Android app and I am using emojis (emoticons) that displays in EditText and TextView with SpannableString.
For that I made a class (code is below). Also I made a gridview which loads all the emojis. The problem is that all works is very slow (because I have 500 emojis) It takes a lot to load and display the emojis. Below is the code I'm using.
I am looking for a better algorithm to replace the String with an emoji or another way load the emojis faster.
public class EmoticonHandler {
private static final Map<String, Integer> emoticons = new HashMap<String, Integer>();
private static void addPattern(Map<String, Integer> map, String smile,
int resource) {
map.put(smile, resource);
}
// Add the items to the HasMap
static {
// Smileys
addPattern(emoticons, "#ce001#", R.drawable._ce001_);
addPattern(emoticons, "#ce002#", R.drawable._ce002_);
addPattern(emoticons, "#ce003#", R.drawable._ce003_);
addPattern(emoticons, "#ce004#", R.drawable._ce004_);
// Here comes the other 500 emojis
}
// Get image for each text smiles
public static void getSmiledText(Context context, Spannable span, int size) {
int index;
for (index = 0; index < span.length(); index++) {
for (Entry<String, Integer> entry : emoticons.entrySet()) {
int length = entry.getKey().length();
if (index + length > span.length())
continue;
if (span.subSequence(index, index + length).toString()
.equals(entry.getKey())) {
span.setSpan(new EmoticonSpan(context, entry.getValue(),
size), index, index + length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
index += length - 1;
break;
}
}
}
}}
Here is the code for the EmoticonSpan
public class EmoticonSpan extends DynamicDrawableSpan {
private Context context;
private int resourceID;
private int size;
private Drawable drawable;
public EmoticonSpan(Context context, int resourceID, int size) {
super();
this.context = context;
this.resourceID = resourceID;
this.size = size;
}
#Override
public Drawable getDrawable() {
if (drawable == null) {
try {
drawable = context.getResources().getDrawable(resourceID);
drawable.setBounds(0, 0, size, size);
} catch (Exception e) {
// Swallow
}
}
return drawable;
}
}
You can use android guide recommendation to display grid view images smoothly and off your main up thread.
http://developer.android.com/training/displaying-bitmaps/display-bitmap.html#gridview
I had a similar problem. Ended up using Emoji maps. Just cram Emojis on a (single) image of no more than 2048X2048 (the biggest Android can handle), and show that as the input grid. Then calculate the X/Y position and find out which Emoji was pointed by the user.
Just remember to take into consideration different device sizes (mdpi ... xxxdpi), and calculate position with correct scroll, and resize offsets.
Here is good code for you :
public class EmojiKeyboard {
private static final String TAG = "EmojiKeyboard";
private static final String PREF_KEY_HEIGHT_KB = "EmojiKbHeight";
private Context context;
private int screenHeight = -1;
private int emojiKbHeight = -1;
private PopupWindow emojiKeyboardPopup;
private View view;
private SharedPreferences preferences;
public EmojiKeyboard(Context context, View view) {
if (context instanceof Activity) {
this.context = context;
this.view = view;
preferences = context.getSharedPreferences(context.getString(R.string.app_name), Context.MODE_PRIVATE);
//Restore EmojiKeyboard Height
emojiKbHeight = preferences.getInt(PREF_KEY_HEIGHT_KB, -1);
//TODO support less then 11 API, and not perfect resizing when switched the keyboard
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
/*
* Get root view height
* */
screenHeight = screenHeight == -1 && bottom > oldBottom
? bottom
: screenHeight;
/*
* Calculate soft keyboard height
* */
int dHeight = oldBottom - bottom;
boolean validHeight = emojiKbHeight == -1 && dHeight > 80 && bottom != oldBottom;
/*
* Сheck twice because the keyboard may have been switched
* */
emojiKbHeight = validHeight
? dHeight : emojiKbHeight != (dHeight) && dHeight > 0
? dHeight
: emojiKbHeight;
/*
* Store emoji keyboard height into SharedPreferences
* */
preferences.edit().putInt(PREF_KEY_HEIGHT_KB, emojiKbHeight).commit();
/*
* If layout returned to a standard height then dismissing keyboard (OnBackPressed)
* */
if (screenHeight == bottom) {
dismissEmojiKeyboard();
}
/*
* Resize emoji on the go when a user switches between keyboards
* */
resizeEmoji();
}
});
}
}
public void showEmoji() {
if (emojiKeyboardPopup == null) {
createEmojiKeyboard();
}
if (!isShowed()) {
new Handler().postDelayed(new Runnable() {
public void run() {
emojiKeyboardPopup.showAtLocation(view, Gravity.BOTTOM, 0, 0);
resizeEmoji();
}
}, 10L);
} else {
dismissEmojiKeyboard();
}
}
public void createEmojiKeyboard() {
EmojiView emojiKeyboard = new EmojiView(context, EmojiView.EMOJI_DARK_STYLE, new EmojiView.onEmojiClickListener() {
public void onBackspace() {
if (((Activity) context).getWindow().getCurrentFocus() instanceof EditText) {
((Activity) context).getWindow().getCurrentFocus().dispatchKeyEvent(new KeyEvent(0, 67));
}
}
public void onEmojiSelected(Emojicon emojicon) {
if (((Activity) context).getWindow().getCurrentFocus() instanceof EditText) {
EmojiView.input((EditText) ((Activity) context).getWindow().getCurrentFocus(), emojicon);
}
}
});
emojiKeyboardPopup = new PopupWindow(emojiKeyboard);
emojiKeyboardPopup.setHeight(View.MeasureSpec.makeMeasureSpec(setEmojiKeyboardHeight(), View.MeasureSpec.EXACTLY));
emojiKeyboardPopup.setWidth(View.MeasureSpec.makeMeasureSpec(getDisplayDimensions(context).x, View.MeasureSpec.EXACTLY));
emojiKeyboardPopup.setAnimationStyle(0);
}
public void dismissEmojiKeyboard() {
if (isShowed()) {
emojiKeyboardPopup.dismiss();
}
}
public boolean isShowed() {
return emojiKeyboardPopup != null && emojiKeyboardPopup.isShowing();
}
/*
* Emoji set up size
* */
public void resizeEmoji() {
if (isShowed()) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) emojiKeyboardPopup.getContentView().getLayoutParams();
layoutParams.height = setEmojiKeyboardHeight();
wm.updateViewLayout(emojiKeyboardPopup.getContentView(), layoutParams);
}
}
public int setEmojiKeyboardHeight() {
return emojiKbHeight == -1 && emojiKbHeight != screenHeight && emojiKbHeight < 80
? (getDisplayDimensions(context).y / 2)
: emojiKbHeight;
}
public Point getDisplayDimensions(Context context) {
Point size = new Point();
WindowManager w = ((Activity) context).getWindowManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
w.getDefaultDisplay().getSize(size);
} else {
Display d = w.getDefaultDisplay();
size.x = d.getWidth();
size.y = d.getHeight();
}
return size;
}
}

Fast Scroll display problem with ListAdapter and SectionIndexer

I have a list of events which are seperated by month and year (Jun 2010, Jul 2010 etc.). I have enabled fast scrolling because the list is really long. I've also implemented SectionIndexer so that people can see what month and year they are currently viewing when scrolling down the list of events at speed.
I don't have any problem with the implementation, just how the information is shown. Fast scrolling with SectionIndexer seems to only really be able to support a label with a single letter. If the list was alphabetised this would be perfect, however I want it to display a bit more text.
If you look at the screenshot bellow you'll see the problem I'm having.
(source: matto1990.com)
What I want to know is: is it possible to change how the text in the centre of the screen is displayed. Can I change it somehow to make it look right (with the background covering all of the text).
Thanks in advance. If you need any clarification, or code just ask.
EDIT: Full sample code for this solution available here.
I had this same problem - I needed to display full text in the overlay rectangle rather than just a single character. I managed to solve it using the following code as an example: http://code.google.com/p/apps-for-android/source/browse/trunk/RingsExtended/src/com/example/android/rings_extended/FastScrollView.java
The author said that this was copied from the Contacts app, which apparently uses its own implementation rather than just setting fastScrollEnabled="true" on the ListView. I altered it a little bit so that you can customize the overlay rectangle width, overlay rectangle height, overlay text size, and scroll thumb width.
For the record, the final result looks like this: http://nolanwlawson.files.wordpress.com/2011/03/pokedroid_1.png
All you need to do is add these values to your res/values/attrs.xml:
<declare-styleable name="CustomFastScrollView">
<attr name="overlayWidth" format="dimension"/>
<attr name="overlayHeight" format="dimension"/>
<attr name="overlayTextSize" format="dimension"/>
<attr name="overlayScrollThumbWidth" format="dimension"/>
</declare-styleable>
And then use this CustomFastScrollView instead of the one in the link:
public class CustomFastScrollView extends FrameLayout
implements OnScrollListener, OnHierarchyChangeListener {
private Drawable mCurrentThumb;
private Drawable mOverlayDrawable;
private int mThumbH;
private int mThumbW;
private int mThumbY;
private RectF mOverlayPos;
// custom values I defined
private int mOverlayWidth;
private int mOverlayHeight;
private float mOverlayTextSize;
private int mOverlayScrollThumbWidth;
private boolean mDragging;
private ListView mList;
private boolean mScrollCompleted;
private boolean mThumbVisible;
private int mVisibleItem;
private Paint mPaint;
private int mListOffset;
private Object [] mSections;
private String mSectionText;
private boolean mDrawOverlay;
private ScrollFade mScrollFade;
private Handler mHandler = new Handler();
private BaseAdapter mListAdapter;
private boolean mChangedBounds;
public static interface SectionIndexer {
Object[] getSections();
int getPositionForSection(int section);
int getSectionForPosition(int position);
}
public CustomFastScrollView(Context context) {
super(context);
init(context, null);
}
public CustomFastScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CustomFastScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void useThumbDrawable(Drawable drawable) {
mCurrentThumb = drawable;
mThumbW = mOverlayScrollThumbWidth;//mCurrentThumb.getIntrinsicWidth();
mThumbH = mCurrentThumb.getIntrinsicHeight();
mChangedBounds = true;
}
private void init(Context context, AttributeSet attrs) {
// set all attributes from xml
if (attrs != null) {
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.CustomFastScrollView);
mOverlayHeight = typedArray.getDimensionPixelSize(
R.styleable.CustomFastScrollView_overlayHeight, 0);
mOverlayWidth = typedArray.getDimensionPixelSize(
R.styleable.CustomFastScrollView_overlayWidth, 0);
mOverlayTextSize = typedArray.getDimensionPixelSize(
R.styleable.CustomFastScrollView_overlayTextSize, 0);
mOverlayScrollThumbWidth = typedArray.getDimensionPixelSize(
R.styleable.CustomFastScrollView_overlayScrollThumbWidth, 0);
}
// Get both the scrollbar states drawables
final Resources res = context.getResources();
Drawable thumbDrawable = res.getDrawable(R.drawable.scrollbar_handle_accelerated_anim2);
useThumbDrawable(thumbDrawable);
mOverlayDrawable = res.getDrawable(android.R.drawable.alert_dark_frame);
mScrollCompleted = true;
setWillNotDraw(false);
// Need to know when the ListView is added
setOnHierarchyChangeListener(this);
mOverlayPos = new RectF();
mScrollFade = new ScrollFade();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setTextSize(mOverlayTextSize);
mPaint.setColor(0xFFFFFFFF);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
private void removeThumb() {
mThumbVisible = false;
// Draw one last time to remove thumb
invalidate();
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (!mThumbVisible) {
// No need to draw the rest
return;
}
final int y = mThumbY;
final int viewWidth = getWidth();
final CustomFastScrollView.ScrollFade scrollFade = mScrollFade;
int alpha = -1;
if (scrollFade.mStarted) {
alpha = scrollFade.getAlpha();
if (alpha < ScrollFade.ALPHA_MAX / 2) {
mCurrentThumb.setAlpha(alpha * 2);
}
int left = viewWidth - (mThumbW * alpha) / ScrollFade.ALPHA_MAX;
mCurrentThumb.setBounds(left, 0, viewWidth, mThumbH);
mChangedBounds = true;
}
canvas.translate(0, y);
mCurrentThumb.draw(canvas);
canvas.translate(0, -y);
// If user is dragging the scroll bar, draw the alphabet overlay
if (mDragging && mDrawOverlay) {
mOverlayDrawable.draw(canvas);
final Paint paint = mPaint;
float descent = paint.descent();
final RectF rectF = mOverlayPos;
canvas.drawText(mSectionText, (int) (rectF.left + rectF.right) / 2,
(int) (rectF.bottom + rectF.top) / 2 + descent, paint);
} else if (alpha == 0) {
scrollFade.mStarted = false;
removeThumb();
} else {
invalidate(viewWidth - mThumbW, y, viewWidth, y + mThumbH);
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mCurrentThumb != null) {
mCurrentThumb.setBounds(w - mThumbW, 0, w, mThumbH);
}
final RectF pos = mOverlayPos;
pos.left = (w - mOverlayWidth) / 2;
pos.right = pos.left + mOverlayWidth;
pos.top = h / 10; // 10% from top
pos.bottom = pos.top + mOverlayHeight;
mOverlayDrawable.setBounds((int) pos.left, (int) pos.top,
(int) pos.right, (int) pos.bottom);
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
if (totalItemCount - visibleItemCount > 0 && !mDragging) {
mThumbY = ((getHeight() - mThumbH) * firstVisibleItem) / (totalItemCount - visibleItemCount);
if (mChangedBounds) {
final int viewWidth = getWidth();
mCurrentThumb.setBounds(viewWidth - mThumbW, 0, viewWidth, mThumbH);
mChangedBounds = false;
}
}
mScrollCompleted = true;
if (firstVisibleItem == mVisibleItem) {
return;
}
mVisibleItem = firstVisibleItem;
if (!mThumbVisible || mScrollFade.mStarted) {
mThumbVisible = true;
mCurrentThumb.setAlpha(ScrollFade.ALPHA_MAX);
}
mHandler.removeCallbacks(mScrollFade);
mScrollFade.mStarted = false;
if (!mDragging) {
mHandler.postDelayed(mScrollFade, 1500);
}
}
private void getSections() {
Adapter adapter = mList.getAdapter();
if (adapter instanceof HeaderViewListAdapter) {
mListOffset = ((HeaderViewListAdapter)adapter).getHeadersCount();
adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
}
if (adapter instanceof SectionIndexer) {
mListAdapter = (BaseAdapter) adapter;
mSections = ((SectionIndexer) mListAdapter).getSections();
}
}
public void onChildViewAdded(View parent, View child) {
if (child instanceof ListView) {
mList = (ListView)child;
mList.setOnScrollListener(this);
getSections();
}
}
public void onChildViewRemoved(View parent, View child) {
if (child == mList) {
mList = null;
mListAdapter = null;
mSections = null;
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mThumbVisible && ev.getAction() == MotionEvent.ACTION_DOWN) {
if (ev.getX() > getWidth() - mThumbW && ev.getY() >= mThumbY &&
ev.getY() <= mThumbY + mThumbH) {
mDragging = true;
return true;
}
}
return false;
}
private void scrollTo(float position) {
int count = mList.getCount();
mScrollCompleted = false;
final Object[] sections = mSections;
int sectionIndex;
if (sections != null && sections.length > 1) {
final int nSections = sections.length;
int section = (int) (position * nSections);
if (section >= nSections) {
section = nSections - 1;
}
sectionIndex = section;
final SectionIndexer baseAdapter = (SectionIndexer) mListAdapter;
int index = baseAdapter.getPositionForSection(section);
// Given the expected section and index, the following code will
// try to account for missing sections (no names starting with..)
// It will compute the scroll space of surrounding empty sections
// and interpolate the currently visible letter's range across the
// available space, so that there is always some list movement while
// the user moves the thumb.
int nextIndex = count;
int prevIndex = index;
int prevSection = section;
int nextSection = section + 1;
// Assume the next section is unique
if (section < nSections - 1) {
nextIndex = baseAdapter.getPositionForSection(section + 1);
}
// Find the previous index if we're slicing the previous section
if (nextIndex == index) {
// Non-existent letter
while (section > 0) {
section--;
prevIndex = baseAdapter.getPositionForSection(section);
if (prevIndex != index) {
prevSection = section;
sectionIndex = section;
break;
}
}
}
// Find the next index, in case the assumed next index is not
// unique. For instance, if there is no P, then request for P's
// position actually returns Q's. So we need to look ahead to make
// sure that there is really a Q at Q's position. If not, move
// further down...
int nextNextSection = nextSection + 1;
while (nextNextSection < nSections &&
baseAdapter.getPositionForSection(nextNextSection) == nextIndex) {
nextNextSection++;
nextSection++;
}
// Compute the beginning and ending scroll range percentage of the
// currently visible letter. This could be equal to or greater than
// (1 / nSections).
float fPrev = (float) prevSection / nSections;
float fNext = (float) nextSection / nSections;
index = prevIndex + (int) ((nextIndex - prevIndex) * (position - fPrev)
/ (fNext - fPrev));
// Don't overflow
if (index > count - 1) index = count - 1;
mList.setSelectionFromTop(index + mListOffset, 0);
} else {
int index = (int) (position * count);
mList.setSelectionFromTop(index + mListOffset, 0);
sectionIndex = -1;
}
if (sectionIndex >= 0) {
String text = mSectionText = sections[sectionIndex].toString();
mDrawOverlay = (text.length() != 1 || text.charAt(0) != ' ') &&
sectionIndex < sections.length;
} else {
mDrawOverlay = false;
}
}
private void cancelFling() {
// Cancel the list fling
MotionEvent cancelFling = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
mList.onTouchEvent(cancelFling);
cancelFling.recycle();
}
#Override
public boolean onTouchEvent(MotionEvent me) {
if (me.getAction() == MotionEvent.ACTION_DOWN) {
if (me.getX() > getWidth() - mThumbW
&& me.getY() >= mThumbY
&& me.getY() <= mThumbY + mThumbH) {
mDragging = true;
if (mListAdapter == null && mList != null) {
getSections();
}
cancelFling();
return true;
}
} else if (me.getAction() == MotionEvent.ACTION_UP) {
if (mDragging) {
mDragging = false;
final Handler handler = mHandler;
handler.removeCallbacks(mScrollFade);
handler.postDelayed(mScrollFade, 1000);
return true;
}
} else if (me.getAction() == MotionEvent.ACTION_MOVE) {
if (mDragging) {
final int viewHeight = getHeight();
mThumbY = (int) me.getY() - mThumbH + 10;
if (mThumbY < 0) {
mThumbY = 0;
} else if (mThumbY + mThumbH > viewHeight) {
mThumbY = viewHeight - mThumbH;
}
// If the previous scrollTo is still pending
if (mScrollCompleted) {
scrollTo((float) mThumbY / (viewHeight - mThumbH));
}
return true;
}
}
return super.onTouchEvent(me);
}
public class ScrollFade implements Runnable {
long mStartTime;
long mFadeDuration;
boolean mStarted;
static final int ALPHA_MAX = 200;
static final long FADE_DURATION = 200;
void startFade() {
mFadeDuration = FADE_DURATION;
mStartTime = SystemClock.uptimeMillis();
mStarted = true;
}
int getAlpha() {
if (!mStarted) {
return ALPHA_MAX;
}
int alpha;
long now = SystemClock.uptimeMillis();
if (now > mStartTime + mFadeDuration) {
alpha = 0;
} else {
alpha = (int) (ALPHA_MAX - ((now - mStartTime) * ALPHA_MAX) / mFadeDuration);
}
return alpha;
}
public void run() {
if (!mStarted) {
startFade();
invalidate();
}
if (getAlpha() > 0) {
final int y = mThumbY;
final int viewWidth = getWidth();
invalidate(viewWidth - mThumbW, y, viewWidth, y + mThumbH);
} else {
mStarted = false;
removeThumb();
}
}
}
}
You can also tweak the translucency of the scroll thumb using ALPHA_MAX.
Then put something like this in your layout xml file:
<com.myapp.CustomFastScrollView android:layout_width="wrap_content"
android:layout_height="fill_parent"
myapp:overlayWidth="175dp" myapp:overlayHeight="110dp" myapp:overlayTextSize="36dp"
myapp:overlayScrollThumbWidth="60dp" android:id="#+id/fast_scroll_view">
<ListView android:id="#android:id/list" android:layout_width="wrap_content"
android:layout_height="fill_parent"/>
<TextView android:id="#android:id/empty"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="" />
</com.myapp.CustomFastScrollView>
Don't forget to declare your attributes in that layout xml file as well:
... xmlns:myapp= "http://schemas.android.com/apk/res/com.myapp" ...
You'll also need to grab the R.drawable.scrollbar_handle_accelerated_anim2 drawables from that Android source code. The link above only contains the mdpi one.
The FastScroller widget is responsible for drawing the overlay. You should probably take a look at its source:
https://android.googlesource.com/platform/frameworks/base/+/gingerbread-release/core/java/android/widget/FastScroller.java
Search for comment:
// If user is dragging the scroll bar, draw the alphabet overlay

Categories

Resources