Android: Zoom Out/In of a GridLayout - java

I'm making a minesweeper game. I'm placing all of the buttons I use for the game in a GridLayout.
The buttons eventually push the layout to be bigger than the size of the screen, so I placed the layout in a HorizontalScrollView and a ScrollView. However, when the game ends, I want to zoom out the layout so I can see all of the buttons on screen.
I've posted the relevant files, although the most important content is fragment_grid.xml (the xml file containing the GridLayout) and GridFragment::gameOver() (where I want to put the zoom-out code)
fragment_grid.xml:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:scrollbars="none"
android:overScrollMode="never">
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/grid"
android:columnCount="10"
android:rowCount="10">
</GridLayout>
</HorizontalScrollView>
</ScrollView>
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity"
android:clickable="true"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</RelativeLayout>
GridFragment.java:
public class GridFragment extends Fragment {
GridLayout gridLayout;
public enum GameState{READY, PLAYING, WIN, LOSE}
GameState gameState;
public GridFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
gameState = GameState.READY;
View view = inflater.inflate(R.layout.fragment_grid, container, false);
gridLayout = (GridLayout)view.findViewById(R.id.grid);
MButton[] buttons = generateGrid(100,25);
for(MButton mButton:buttons){
gridLayout.addView(mButton);
}
return view;
}
public MButton[] generateGrid(int gridSize, int numMines){
Log.d("gridSize",""+gridSize);
Log.d("numMines",""+numMines);
ArrayList<Integer> array = new ArrayList<Integer>();
for (int i = 0; i < gridSize; i++) {
array.add(new Integer(i));
}
Log.d("unsorted",array.toString());
Collections.shuffle(array,new Random(System.currentTimeMillis()));
MButton[] mButtons = new MButton[gridSize];
MButton mButton;
String numList = array.toString();
Log.d("arraySize",""+array.size());
Log.d("array",numList);
for (int i = 0; i < gridSize; i++) {
int x = array.get(i);
mButton = new MButton(getActivity(),i, numMines > x);
mButtons[i] = mButton;
}
return generateMineCount(mButtons);
}
private MButton[] generateMineCount(MButton[] mButtons){
int rows = gridLayout.getRowCount();
int columns = gridLayout.getColumnCount();
for (int i = 0; i < mButtons.length; i++) {
if((i/columns) > 0){if(mButtons[i-columns].isMine()){mButtons[i].addAdjacentMines();}}
if((i/columns) < rows-1){if(mButtons[i+columns].isMine()){mButtons[i].addAdjacentMines();}}
if((i%rows) > 0){if(mButtons[i-1].isMine()){mButtons[i].addAdjacentMines();}}
if((i%rows) < columns-1){if(mButtons[i+1].isMine()){mButtons[i].addAdjacentMines();}}
if( ((i/columns) > 0)&&((i%rows) > 0) ){if(mButtons[i-columns-1].isMine()){mButtons[i].addAdjacentMines();}}
if( ((i/columns) < rows-1)&&((i%rows) > 0) ){if(mButtons[i+columns-1].isMine()){mButtons[i].addAdjacentMines();}}
if( ((i/columns) > 0)&&((i%rows) < columns-1) ){if(mButtons[i-columns+1].isMine()){mButtons[i].addAdjacentMines();}}
if( ((i/columns) < rows-1)&&((i%rows) < columns-1) ){if(mButtons[i+columns+1].isMine()){mButtons[i].addAdjacentMines();}}
if(!mButtons[i].isMine() && mButtons[i].getAdjacentMines() > 0){mButtons[i].displayMines();}
}
return mButtons;
}
public void startGame(){
gameState = GameState.PLAYING;
}
public void gameOver(){
gameState = GameState.LOSE;
for (int i = 0; i < gridLayout.getChildCount(); i++) {
MButton mb = (MButton)gridLayout.getChildAt(i);
mb.setOnTouchListener(null);
//insert zoom-out layout code here
}
gridLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
MButton.java:
public class MButton extends Button {
public enum State{NORMAL, OPENED, FLAGGED, UNKNOWN}
State state;
boolean longPress;
long startTime = 0;
int num;
boolean mine;
final float scale = getContext().getResources().getDisplayMetrics().density;
int adjacentMines = 0;
MainActivity mainActivity = (MainActivity)getContext();
GridFragment gridFragment = (GridFragment)mainActivity.getFragmentManager().findFragmentByTag("gridFragment");
ImageButton startButton = (ImageButton)(mainActivity.getActionBar()).getCustomView().findViewById(R.id.actionBarLogo);
static Handler timerHandler = new Handler();
Runnable timerRunnable = new Runnable() {
#Override
public void run() {
long milliseconds = System.currentTimeMillis() - startTime;
longPress = false;
if(milliseconds >= 1500) {
longPressTile();
timerHandler.removeCallbacks(timerRunnable);
}
else
timerHandler.postDelayed(this, 0);
}
};
public MButton(Context context, int i, boolean m) {
super(context);
create(i, m);
}
public MButton(Context context, AttributeSet attrs, int i, boolean m) {
super(context, attrs);
create(i, m);
}
public MButton(Context context, AttributeSet attrs, int defStyleAttr, int i, boolean m) {
super(context, attrs, defStyleAttr);
create(i, m);
}
private void longPressTile(){
longPress = true;
setBackgroundResource(R.drawable.tile);
startButton.setImageResource(R.drawable.smiley);
if(state == State.FLAGGED || state == State.UNKNOWN){
state = State.NORMAL;
setText("");
}
else if(state == State.NORMAL){
state = State.FLAGGED;
setText("F");
}
}
private boolean isInGrayArea(float xCoord, float yCoord){
int xValue = (int) ((xCoord/scale)+0.5);
int yValue = (int) ((yCoord/scale)+0.5);
Log.d("button"+num,"("+xValue+","+yValue+")");
Log.d("size","("+Integer.toString(getWidth())+","+Integer.toString(getHeight())+")");
return (xValue >= 5)&&(xValue < 45)&&(yValue >= 5)&&(yValue < 45);
}
public void addAdjacentMines(){
adjacentMines++;
}
public boolean isMine(){
return mine;
}
public void displayMines(){
if(mine)
setText("M");
else if(hasAdjacentMines())
setText(""+adjacentMines);
else
setText("");
}
public int getAdjacentMines(){
return adjacentMines;
}
public boolean hasAdjacentMines(){ return getAdjacentMines() > 0;}
private void openAdjacentButtons(){
GridLayout gridLayout = gridFragment.gridLayout;
int rows = gridLayout.getRowCount();
int columns = gridLayout.getColumnCount();
if((num/columns) > 0){((MButton)gridLayout.getChildAt(num-columns)).revealButton();}
if((num/columns) < rows-1){((MButton)gridLayout.getChildAt(num+columns)).revealButton();}
if((num%rows) > 0){((MButton)gridLayout.getChildAt(num-1)).revealButton();}
if((num%rows) < columns-1){((MButton)gridLayout.getChildAt(num+1)).revealButton();}//
if( ((num/columns) > 0)&&((num%rows) > 0) ){((MButton)gridLayout.getChildAt(num-columns-1)).revealButton();}
if( ((num/columns) < rows-1)&&((num%rows) > 0) ){((MButton)gridLayout.getChildAt(num+columns-1)).revealButton();}
if( ((num/columns) > 0)&&((num%rows) < columns-1) ){((MButton)gridLayout.getChildAt(num-columns+1)).revealButton();}
if( ((num/columns) < rows-1)&&((num%rows) < columns-1) ){((MButton)gridLayout.getChildAt(num+columns+1)).revealButton();}
}
public void revealButton() {
if (state != State.OPENED){
state = State.OPENED;
displayMines();
if (!isMine() && !hasAdjacentMines())
openAdjacentButtons();
else if(isMine()){
startButton.setImageResource(R.drawable.smiley3);
gridFragment.gameOver();
}
setBackgroundResource(R.drawable.tile3);
}
}
private void create(int i, boolean m){
state = State.NORMAL;
num = i;
mine = m;
setBackgroundResource(R.drawable.tile);
setLayoutParams(new LinearLayout.LayoutParams(150,150));
if(mine){
setText("m");
}
this.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent me) {
if(isInGrayArea(me.getX(), me.getY())){
switch (me.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (state != State.OPENED) {
if(gridFragment.gameState == GridFragment.GameState.READY)
gridFragment.startGame();
startTime = System.currentTimeMillis();
v.setBackgroundResource(R.drawable.tile2);
startButton.setImageResource(R.drawable.smiley2);
timerHandler.postDelayed(timerRunnable, 0);
}
return true;
}
case MotionEvent.ACTION_UP: {
if (!longPress) {
startButton.setImageResource(R.drawable.smiley);
switch(state){
case NORMAL:{
revealButton();
break;
}
case UNKNOWN:{
state = State.UNKNOWN;
v.setBackgroundResource(R.drawable.tile);
setText("?");
break;
}
case FLAGGED:{
state = State.FLAGGED;
v.setBackgroundResource(R.drawable.tile);
setText("F");
}
}
}
timerHandler.removeCallbacks(timerRunnable);
return true;
}
}
}
return false;
}
});
}
}
MainActivity.java
public class MainActivity extends Activity {
private boolean mInit = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ActionBar actionBar = getActionBar();
actionBar.setCustomView(R.layout.actionbar);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayUseLogoEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
Typeface myTypeface = Typeface.createFromAsset(getAssets(), "fonts/digital-7.ttf");
TextView myTextView = (TextView)findViewById(R.id.textView1);
TextView myTopTextView = (TextView)findViewById(R.id.topTextView1);
myTextView.setTypeface(myTypeface);
myTopTextView.setTypeface(myTypeface);
if (findViewById(R.id.fragment_container) != null){
if (savedInstanceState != null) {
return;
}
}
}
private void initial(){
GridFragment gFragment = new GridFragment();
gFragment.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().add(R.id.fragment_container, gFragment,"gridFragment").commit();
}
#Override
protected void onStart() {
if (!mInit) {
mInit = true;
initial();
}
super.onStart();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
P.S. Happy New Year's!

i guess this would help: Android - zoom in/out RelativeLayout with spread/pinch
you can applay the same implementation for gridLayout good luck :) and Happy new year

I know its been 6 months I spent hours trying to make this work.
This class should work for any view or viewgroup. Just extend whatever view or viewgroup needs to zoom.
https://github.com/Xjasz/AndroidZoomableViewGroup

Related

Android - NullPointerException SimpleAdapter - Pinned section ListView?

I created a ListView with pinned which works well once. But when I arrive latest item of ListView crashes on me and says NullPointerException.
I need that when I arrive latest item get next 5 of items .
MainActivity.java :
public class PinnedSectionListActivity extends ListActivity implements OnClickListener {
public static Context context;
public static Context _context;
public static SQLiteDatabase sql;
public Cursor cursor;
public Cursor cursorA;
public boolean flag = false;
public Integer count = 0;
public List<Items> result;
public List<ItemsT> resultT;
static class SimpleAdapter extends ArrayAdapter<Item> implements PinnedSectionListView.PinnedSectionListAdapter {
private List<Items> Results;
private List<ItemsT> ResultsT;
private Integer counts;
private static final int[] COLORS = new int[] {
R.color.green_light, R.color.orange_light,
R.color.blue_light, R.color.red_light };
public SimpleAdapter(Context context,List<Items> MResults,List<ItemsT> MresultT,Integer count , int resource, int textViewResourceId) {
super(context, resource, textViewResourceId);
this.Results = MResults;
this.ResultsT = MresultT;
this.counts = count;
generateDataset(false);
}
public void generateDataset(boolean clear) {
int y = 0;
if (clear) clear();
prepareSections(counts);
int sectionPosition = 0, listPosition = 0;
for (char i=0; i<counts; i++) {
final Items ci = Results.get(i);
Item section = new Item(Item.SECTION, ci.WTitleO);
section.sectionPosition = sectionPosition;
section.listPosition = listPosition++;
onSectionAdded(section, sectionPosition);
add(section);
for (int j=0;j<ci.count;j++) {
final ItemsT xx = ResultsT.get(y);
Item item = new Item(Item.ITEM, xx.WTitleT);
item.sectionPosition = sectionPosition;
item.listPosition = listPosition++;
y++;
add(item);
}
sectionPosition++;
}
}
protected void prepareSections(int sectionsNumber) { }
protected void onSectionAdded(Item section, int sectionPosition) { }
#Override public View getView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView) super.getView(position, convertView, parent);
view.setTextColor(Color.DKGRAY);
view.setTag("" + position);
Item item = getItem(position);
if (item.type == Item.SECTION) {
view.setBackgroundColor(parent.getResources().getColor(COLORS[item.sectionPosition % COLORS.length]));
}
return view;
}
#Override public int getViewTypeCount() {
return 2;
}
#Override public int getItemViewType(int position) {
return getItem(position).type;
}
#Override
public boolean isItemViewTypePinned(int viewType) {
return viewType == Item.SECTION;
}
}
static class Items {
public int conO ;
public String WTitleO;
public int count;
}
static class ItemsT {
public String WTitleT;
}
static class Item {
public static final int ITEM = 0;
public static final int SECTION = 1;
public final int type;
public final String text;
public int sectionPosition;
public int listPosition;
public Item(int type, String text) {
this.type = type;
this.text = text;
}
#Override public String toString() {
return text;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
_context = this;
DB db = new DB(context);
db.CreateFile();
try {
db.CreateandOpenDataBase();
} catch (IOException e) {
e.printStackTrace();
}
sql = db.openDataBase();
test();
}
public void test(){
result = new ArrayList<Items>();
resultT = new ArrayList<ItemsT>();
try {
cursor = sql.rawQuery("SELECT (select count() as number from WebSite_BookPageDB22 as b where b.ParentID = a.ContentID) as count" +
" ,ContentID,Title" +
" FROM WebSite_BookPageDB22 as a" +
" WHERE ArticleID = '" + 61799 + "' AND ParentID = '" + 0 + "'order by PageID ASC LIMIT 5", null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
Items ci = new Items();
ci.conO = cursor.getInt(cursor.getColumnIndex("ContentID"));
ci.WTitleO = cursor.getString(cursor.getColumnIndex("Title"));
ci.count = cursor.getInt(cursor.getColumnIndex("count"));
if(flag == false) {
count = cursor.getCount();
flag = true;
}
cursorA = sql.rawQuery("select Title from WebSite_BookPageDB22 where ParentID = '" + ci.conO +"' order by PageID ASC", null);
try {
if (cursorA != null && cursorA.moveToFirst()) {
do {
ItemsT cty = new ItemsT();
cty.WTitleT = cursorA.getString(cursorA.getColumnIndex("Title"));
resultT.add(cty);
} while (cursorA.moveToNext());
}
}catch (Exception e){
Log.i("xxx", "You have an error");
}finally {
if (cursorA != null) {
cursorA.close();
}
}
result.add(ci);
} while (cursor.moveToNext());
}
}
} catch (Exception e) {
} finally {
cursor.close();
}
SimpleAdapter adapt = new SimpleAdapter(_context, result, resultT, count, android.R.layout.simple_list_item_1, android.R.id.text1);
setListAdapter(adapt);
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Item item = (Item) getListView().getAdapter().getItem(position);
if (item != null) {
Toast.makeText(this, "Item " + position + ": " + item.text, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Item " + position, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onClick(View v) {
Toast.makeText(this, "Item: " + v.getTag() , Toast.LENGTH_SHORT).show();
}
}
And PinnedSectionListView.java :
public class PinnedSectionListView extends ListView {
//-- inner classes
/** List adapter to be implemented for being used with PinnedSectionListView adapter. */
public static interface PinnedSectionListAdapter extends ListAdapter {
/** This method shall return 'true' if views of given type has to be pinned. */
boolean isItemViewTypePinned(int viewType);
}
/** Wrapper class for pinned section view and its position in the list. */
static class PinnedSection {
public View view;
public int position;
public long id;
}
//-- class fields
// fields used for handling touch events
private final Rect mTouchRect = new Rect();
private final PointF mTouchPoint = new PointF();
private int mTouchSlop;
private View mTouchTarget;
private MotionEvent mDownEvent;
// fields used for drawing shadow under a pinned section
private GradientDrawable mShadowDrawable;
private int mSectionsDistanceY;
private int mShadowHeight;
/** Delegating listener, can be null. */
OnScrollListener mDelegateOnScrollListener;
/** Shadow for being recycled, can be null. */
PinnedSection mRecycleSection;
/** shadow instance with a pinned view, can be null. */
PinnedSection mPinnedSection;
/** Pinned view Y-translation. We use it to stick pinned view to the next section. */
int mTranslateY;
/** Scroll listener which does the magic */
private final OnScrollListener mOnScrollListener = new OnScrollListener() {
#Override public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if(firstVisibleItem+visibleItemCount == totalItemCount && totalItemCount!=0)
{
PinnedSectionListActivity xx = new PinnedSectionListActivity();
xx.flag = false;
xx.test();
/*Intent refresh = new Intent(PinnedSectionListActivity._context, PinnedSectionListActivity.class);
PinnedSectionListActivity._context.startActivity(refresh);*/
}
// get expected adapter or fail fast
ListAdapter adapter = getAdapter();
if (adapter == null || visibleItemCount == 0) return; // nothing to do
final boolean isFirstVisibleItemSection =
isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
if (isFirstVisibleItemSection) {
View sectionView = getChildAt(0);
if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
destroyPinnedShadow();
} else { // section doesn't stick to the top, make sure we have a pinned shadow
ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
}
} else { // section is not at the first visible position
int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
if (sectionPosition > -1) { // we have section position
ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
} else { // there is no section for the first visible item, destroy shadow
destroyPinnedShadow();
}
}
};
};
/** Default change observer. */
private final DataSetObserver mDataSetObserver = new DataSetObserver() {
#Override public void onChanged() {
recreatePinnedShadow();
};
#Override public void onInvalidated() {
recreatePinnedShadow();
}
};
//-- constructors
public PinnedSectionListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
setOnScrollListener(mOnScrollListener);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
initShadow(true);
}
//-- public API methods
public void setShadowVisible(boolean visible) {
initShadow(visible);
if (mPinnedSection != null) {
View v = mPinnedSection.view;
invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);
}
}
//-- pinned section drawing methods
public void initShadow(boolean visible) {
if (visible) {
if (mShadowDrawable == null) {
mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,
new int[] { Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});
mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);
}
} else {
if (mShadowDrawable != null) {
mShadowDrawable = null;
mShadowHeight = 0;
}
}
}
/** Create shadow wrapper with a pinned view for a view at given position */
void createPinnedShadow(int position) {
// try to recycle shadow
PinnedSection pinnedShadow = mRecycleSection;
mRecycleSection = null;
// create new shadow, if needed
if (pinnedShadow == null) pinnedShadow = new PinnedSection();
// request new view using recycled view, if such
View pinnedView = getAdapter().getView(position, pinnedShadow.view, PinnedSectionListView.this);
// read layout parameters
LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
if (layoutParams == null) {
layoutParams = (LayoutParams) generateDefaultLayoutParams();
pinnedView.setLayoutParams(layoutParams);
}
int heightMode = MeasureSpec.getMode(layoutParams.height);
int heightSize = MeasureSpec.getSize(layoutParams.height);
if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;
int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
if (heightSize > maxHeight) heightSize = maxHeight;
// measure & layout
int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
pinnedView.measure(ws, hs);
pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());
mTranslateY = 0;
// initialize pinned shadow
pinnedShadow.view = pinnedView;
pinnedShadow.position = position;
pinnedShadow.id = getAdapter().getItemId(position);
// store pinned shadow
mPinnedSection = pinnedShadow;
}
/** Destroy shadow wrapper for currently pinned view */
void destroyPinnedShadow() {
if (mPinnedSection != null) {
// keep shadow for being recycled later
mRecycleSection = mPinnedSection;
mPinnedSection = null;
}
}
/** Makes sure we have an actual pinned shadow for given position. */
void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {
if (visibleItemCount < 2) { // no need for creating shadow at all, we have a single visible item
destroyPinnedShadow();
return;
}
if (mPinnedSection != null
&& mPinnedSection.position != sectionPosition) { // invalidate shadow, if required
destroyPinnedShadow();
}
if (mPinnedSection == null) { // create shadow, if empty
createPinnedShadow(sectionPosition);
}
// align shadow according to next section position, if needed
int nextPosition = sectionPosition + 1;
if (nextPosition < getCount()) {
int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,
visibleItemCount - (nextPosition - firstVisibleItem));
if (nextSectionPosition > -1) {
View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);
final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
mSectionsDistanceY = nextSectionView.getTop() - bottom;
if (mSectionsDistanceY < 0) {
// next section overlaps pinned shadow, move it up
mTranslateY = mSectionsDistanceY;
} else {
// next section does not overlap with pinned, stick to top
mTranslateY = 0;
}
} else {
// no other sections are visible, stick to top
mTranslateY = 0;
mSectionsDistanceY = Integer.MAX_VALUE;
}
}
}
int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {
ListAdapter adapter = getAdapter();
int adapterDataCount = adapter.getCount();
if (getLastVisiblePosition() >= adapterDataCount) return -1; // dataset has changed, no candidate
if (firstVisibleItem+visibleItemCount >= adapterDataCount){//added to prevent index Outofbound (in case)
visibleItemCount = adapterDataCount-firstVisibleItem;
}
for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {
int position = firstVisibleItem + childIndex;
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1;
}
int findCurrentSectionPosition(int fromPosition) {
ListAdapter adapter = getAdapter();
if (fromPosition >= adapter.getCount()) return -1; // dataset has changed, no candidate
if (adapter instanceof SectionIndexer) {
// try fast way by asking section indexer
SectionIndexer indexer = (SectionIndexer) adapter;
int sectionPosition = indexer.getSectionForPosition(fromPosition);
int itemPosition = indexer.getPositionForSection(sectionPosition);
int typeView = adapter.getItemViewType(itemPosition);
if (isItemViewTypePinned(adapter, typeView)) {
return itemPosition;
} // else, no luck
}
// try slow way by looking through to the next section item above
for (int position=fromPosition; position>=0; position--) {
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1; // no candidate found
}
void recreatePinnedShadow() {
destroyPinnedShadow();
ListAdapter adapter = getAdapter();
if (adapter != null && adapter.getCount() > 0) {
int firstVisiblePosition = getFirstVisiblePosition();
int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);
if (sectionPosition == -1) return; // no views to pin, exit
ensureShadowForPosition(sectionPosition,
firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);
}
}
#Override
public void setOnScrollListener(OnScrollListener listener) {
if (listener == mOnScrollListener) {
super.setOnScrollListener(listener);
} else {
mDelegateOnScrollListener = listener;
}
}
#Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
post(new Runnable() {
#Override public void run() { // restore pinned view after configuration change
recreatePinnedShadow();
}
});
}
#Override
public void setAdapter(ListAdapter adapter) {
// assert adapter in debug mode
if (BuildConfig.DEBUG && adapter != null) {
if (!(adapter instanceof PinnedSectionListAdapter))
throw new IllegalArgumentException("Does your adapter implement PinnedSectionListAdapter?");
if (adapter.getViewTypeCount() < 2)
throw new IllegalArgumentException("Does your adapter handle at least two types" +
" of views in getViewTypeCount() method: items and sections?");
}
// unregister observer at old adapter and register on new one
ListAdapter oldAdapter = getAdapter();
if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);
if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);
// destroy pinned shadow, if new adapter is not same as old one
if (oldAdapter != adapter) destroyPinnedShadow();
super.setAdapter(adapter);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mPinnedSection != null) {
int parentWidth = r - l - getPaddingLeft() - getPaddingRight();
int shadowWidth = mPinnedSection.view.getWidth();
if (parentWidth != shadowWidth) {
recreatePinnedShadow();
}
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mPinnedSection != null) {
// prepare variables
int pLeft = getListPaddingLeft();
int pTop = getListPaddingTop();
View view = mPinnedSection.view;
// draw child
canvas.save();
int clipHeight = view.getHeight() +
(mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));
canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);
canvas.translate(pLeft, pTop + mTranslateY);
drawChild(canvas, mPinnedSection.view, getDrawingTime());
if (mShadowDrawable != null && mSectionsDistanceY > 0) {
mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),
mPinnedSection.view.getBottom(),
mPinnedSection.view.getRight(),
mPinnedSection.view.getBottom() + mShadowHeight);
mShadowDrawable.draw(canvas);
}
canvas.restore();
}
}
//-- touch handling methods
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN
&& mTouchTarget == null
&& mPinnedSection != null
&& isPinnedViewTouched(mPinnedSection.view, x, y)) { // create touch target
// user touched pinned view
mTouchTarget = mPinnedSection.view;
mTouchPoint.x = x;
mTouchPoint.y = y;
// copy down event for eventually be used later
mDownEvent = MotionEvent.obtain(ev);
}
if (mTouchTarget != null) {
if (isPinnedViewTouched(mTouchTarget, x, y)) { // forward event to pinned view
mTouchTarget.dispatchTouchEvent(ev);
}
if (action == MotionEvent.ACTION_UP) { // perform onClick on pinned view
super.dispatchTouchEvent(ev);
performPinnedItemClick();
clearTouchTarget();
} else if (action == MotionEvent.ACTION_CANCEL) { // cancel
clearTouchTarget();
} else if (action == MotionEvent.ACTION_MOVE) {
if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {
// cancel sequence on touch target
MotionEvent event = MotionEvent.obtain(ev);
event.setAction(MotionEvent.ACTION_CANCEL);
mTouchTarget.dispatchTouchEvent(event);
event.recycle();
// provide correct sequence to super class for further handling
super.dispatchTouchEvent(mDownEvent);
super.dispatchTouchEvent(ev);
clearTouchTarget();
}
}
return true;
}
// call super if this was not our pinned view
return super.dispatchTouchEvent(ev);
}
private boolean isPinnedViewTouched(View view, float x, float y) {
view.getHitRect(mTouchRect);
// by taping top or bottom padding, the list performs on click on a border item.
// we don't add top padding here to keep behavior consistent.
mTouchRect.top += mTranslateY;
mTouchRect.bottom += mTranslateY + getPaddingTop();
mTouchRect.left += getPaddingLeft();
mTouchRect.right -= getPaddingRight();
return mTouchRect.contains((int)x, (int)y);
}
private void clearTouchTarget() {
mTouchTarget = null;
if (mDownEvent != null) {
mDownEvent.recycle();
mDownEvent = null;
}
}
private boolean performPinnedItemClick() {
if (mPinnedSection == null) return false;
OnItemClickListener listener = getOnItemClickListener();
if (listener != null && getAdapter().isEnabled(mPinnedSection.position)) {
View view = mPinnedSection.view;
playSoundEffect(SoundEffectConstants.CLICK);
if (view != null) {
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
}
listener.onItemClick(this, view, mPinnedSection.position, mPinnedSection.id);
return true;
}
return false;
}
public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {
if (adapter instanceof HeaderViewListAdapter) {
adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
}
return ((PinnedSectionListAdapter) adapter).isItemViewTypePinned(viewType);
}
}
For second time get me error here :
SimpleAdapter adapt = new SimpleAdapter(_context, result, resultT, count, android.R.layout.simple_list_item_1, android.R.id.text1);
setListAdapter(adapt);
Your not using PinnedSectionListActivity current reference properly,If you wan to access your current public function inside any other class then you have to pass your current activity reference :
Declaration
private Context context;
Get current activity reference
public PinnedSectionListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
}
public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
initView();
}
Use current activity reference in onScroll() :
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if(firstVisibleItem+visibleItemCount == totalItemCount && totalItemCount!=0)
{
if(context!=null){
((PinnedSectionListActivity)context).cleans();
}
}else {
// get expected adapter or fail fast
ListAdapter adapter = getAdapter();
if (adapter == null || visibleItemCount == 0) return; // nothing to do
final boolean isFirstVisibleItemSection = isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
if (isFirstVisibleItemSection) {
View sectionView = getChildAt(0);
if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
destroyPinnedShadow();
} else { // section doesn't stick to the top, make sure we have a pinned shadow
ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
}
} else { // section is not at the first visible position
int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
if (sectionPosition > -1) { // we have section position
ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
} else { // there is no section for the first visible item, destroy shadow
destroyPinnedShadow();
}
}
}
};
PinnedSectionListView full activity code :
public class PinnedSectionListView extends ListView {
//-- inner classes
/** List adapter to be implemented for being used with PinnedSectionListView adapter. */
public static interface PinnedSectionListAdapter extends ListAdapter {
/** This method shall return 'true' if views of given type has to be pinned. */
boolean isItemViewTypePinned(int viewType);
}
/** Wrapper class for pinned section view and its position in the list. */
static class PinnedSection {
public View view;
public int position;
public long id;
}
//-- class fields
// fields used for handling touch events
private final Rect mTouchRect = new Rect();
private final PointF mTouchPoint = new PointF();
private int mTouchSlop;
private View mTouchTarget;
private MotionEvent mDownEvent;
// fields used for drawing shadow under a pinned section
private GradientDrawable mShadowDrawable;
private int mSectionsDistanceY;
private int mShadowHeight;
/** Delegating listener, can be null. */
OnScrollListener mDelegateOnScrollListener;
/** Shadow for being recycled, can be null. */
PinnedSection mRecycleSection;
/** shadow instance with a pinned view, can be null. */
PinnedSection mPinnedSection;
/** Pinned view Y-translation. We use it to stick pinned view to the next section. */
int mTranslateY;
private Context context;
/** Scroll listener which does the magic */
private final OnScrollListener mOnScrollListener = new OnScrollListener() {
#Override public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if(firstVisibleItem+visibleItemCount == totalItemCount && totalItemCount!=0)
{
if(context!=null){
((PinnedSectionListActivity)context).cleans();
}
}else {
// get expected adapter or fail fast
ListAdapter adapter = getAdapter();
if (adapter == null || visibleItemCount == 0) return; // nothing to do
final boolean isFirstVisibleItemSection = isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));
if (isFirstVisibleItemSection) {
View sectionView = getChildAt(0);
if (sectionView.getTop() == getPaddingTop()) { // view sticks to the top, no need for pinned shadow
destroyPinnedShadow();
} else { // section doesn't stick to the top, make sure we have a pinned shadow
ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);
}
} else { // section is not at the first visible position
int sectionPosition = findCurrentSectionPosition(firstVisibleItem);
if (sectionPosition > -1) { // we have section position
ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);
} else { // there is no section for the first visible item, destroy shadow
destroyPinnedShadow();
}
}
}
};
};
/** Default change observer. */
private final DataSetObserver mDataSetObserver = new DataSetObserver() {
#Override public void onChanged() {
recreatePinnedShadow();
};
#Override public void onInvalidated() {
recreatePinnedShadow();
}
};
//-- constructors
public PinnedSectionListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
}
public PinnedSectionListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
initView();
}
private void initView() {
setOnScrollListener(mOnScrollListener);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
initShadow(true);
}
//-- public API methods
public void setShadowVisible(boolean visible) {
initShadow(visible);
if (mPinnedSection != null) {
View v = mPinnedSection.view;
invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);
}
}
//-- pinned section drawing methods
public void initShadow(boolean visible) {
if (visible) {
if (mShadowDrawable == null) {
mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,
new int[] { Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});
mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);
}
} else {
if (mShadowDrawable != null) {
mShadowDrawable = null;
mShadowHeight = 0;
}
}
}
/** Create shadow wrapper with a pinned view for a view at given position */
void createPinnedShadow(int position) {
// try to recycle shadow
PinnedSection pinnedShadow = mRecycleSection;
mRecycleSection = null;
// create new shadow, if needed
if (pinnedShadow == null) pinnedShadow = new PinnedSection();
// request new view using recycled view, if such
View pinnedView = getAdapter().getView(position, pinnedShadow.view, PinnedSectionListView.this);
// read layout parameters
LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();
if (layoutParams == null) {
layoutParams = (LayoutParams) generateDefaultLayoutParams();
pinnedView.setLayoutParams(layoutParams);
}
int heightMode = MeasureSpec.getMode(layoutParams.height);
int heightSize = MeasureSpec.getSize(layoutParams.height);
if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;
int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();
if (heightSize > maxHeight) heightSize = maxHeight;
// measure & layout
int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);
int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
pinnedView.measure(ws, hs);
pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());
mTranslateY = 0;
// initialize pinned shadow
pinnedShadow.view = pinnedView;
pinnedShadow.position = position;
pinnedShadow.id = getAdapter().getItemId(position);
// store pinned shadow
mPinnedSection = pinnedShadow;
}
/** Destroy shadow wrapper for currently pinned view */
void destroyPinnedShadow() {
if (mPinnedSection != null) {
// keep shadow for being recycled later
mRecycleSection = mPinnedSection;
mPinnedSection = null;
}
}
/** Makes sure we have an actual pinned shadow for given position. */
void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {
if (visibleItemCount < 2) { // no need for creating shadow at all, we have a single visible item
destroyPinnedShadow();
return;
}
if (mPinnedSection != null
&& mPinnedSection.position != sectionPosition) { // invalidate shadow, if required
destroyPinnedShadow();
}
if (mPinnedSection == null) { // create shadow, if empty
createPinnedShadow(sectionPosition);
}
// align shadow according to next section position, if needed
int nextPosition = sectionPosition + 1;
if (nextPosition < getCount()) {
int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,
visibleItemCount - (nextPosition - firstVisibleItem));
if (nextSectionPosition > -1) {
View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);
final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();
mSectionsDistanceY = nextSectionView.getTop() - bottom;
if (mSectionsDistanceY < 0) {
// next section overlaps pinned shadow, move it up
mTranslateY = mSectionsDistanceY;
} else {
// next section does not overlap with pinned, stick to top
mTranslateY = 0;
}
} else {
// no other sections are visible, stick to top
mTranslateY = 0;
mSectionsDistanceY = Integer.MAX_VALUE;
}
}
}
int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {
ListAdapter adapter = getAdapter();
int adapterDataCount = adapter.getCount();
if (getLastVisiblePosition() >= adapterDataCount) return -1; // dataset has changed, no candidate
if (firstVisibleItem+visibleItemCount >= adapterDataCount){//added to prevent index Outofbound (in case)
visibleItemCount = adapterDataCount-firstVisibleItem;
}
for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {
int position = firstVisibleItem + childIndex;
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1;
}
int findCurrentSectionPosition(int fromPosition) {
ListAdapter adapter = getAdapter();
if (fromPosition >= adapter.getCount()) return -1; // dataset has changed, no candidate
if (adapter instanceof SectionIndexer) {
// try fast way by asking section indexer
SectionIndexer indexer = (SectionIndexer) adapter;
int sectionPosition = indexer.getSectionForPosition(fromPosition);
int itemPosition = indexer.getPositionForSection(sectionPosition);
int typeView = adapter.getItemViewType(itemPosition);
if (isItemViewTypePinned(adapter, typeView)) {
return itemPosition;
} // else, no luck
}
// try slow way by looking through to the next section item above
for (int position=fromPosition; position>=0; position--) {
int viewType = adapter.getItemViewType(position);
if (isItemViewTypePinned(adapter, viewType)) return position;
}
return -1; // no candidate found
}
void recreatePinnedShadow() {
destroyPinnedShadow();
ListAdapter adapter = getAdapter();
if (adapter != null && adapter.getCount() > 0) {
int firstVisiblePosition = getFirstVisiblePosition();
int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);
if (sectionPosition == -1) return; // no views to pin, exit
ensureShadowForPosition(sectionPosition,
firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);
}
}
#Override
public void setOnScrollListener(OnScrollListener listener) {
if (listener == mOnScrollListener) {
super.setOnScrollListener(listener);
} else {
mDelegateOnScrollListener = listener;
}
}
#Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
post(new Runnable() {
#Override public void run() { // restore pinned view after configuration change
recreatePinnedShadow();
}
});
}
#Override
public void setAdapter(ListAdapter adapter) {
// assert adapter in debug mode
if (BuildConfig.DEBUG && adapter != null) {
if (!(adapter instanceof PinnedSectionListAdapter))
throw new IllegalArgumentException("Does your adapter implement PinnedSectionListAdapter?");
if (adapter.getViewTypeCount() < 2)
throw new IllegalArgumentException("Does your adapter handle at least two types" +
" of views in getViewTypeCount() method: items and sections?");
}
// unregister observer at old adapter and register on new one
ListAdapter oldAdapter = getAdapter();
if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);
if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);
// destroy pinned shadow, if new adapter is not same as old one
if (oldAdapter != adapter) destroyPinnedShadow();
super.setAdapter(adapter);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mPinnedSection != null) {
int parentWidth = r - l - getPaddingLeft() - getPaddingRight();
int shadowWidth = mPinnedSection.view.getWidth();
if (parentWidth != shadowWidth) {
recreatePinnedShadow();
}
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mPinnedSection != null) {
// prepare variables
int pLeft = getListPaddingLeft();
int pTop = getListPaddingTop();
View view = mPinnedSection.view;
// draw child
canvas.save();
int clipHeight = view.getHeight() +
(mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));
canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);
canvas.translate(pLeft, pTop + mTranslateY);
drawChild(canvas, mPinnedSection.view, getDrawingTime());
if (mShadowDrawable != null && mSectionsDistanceY > 0) {
mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),
mPinnedSection.view.getBottom(),
mPinnedSection.view.getRight(),
mPinnedSection.view.getBottom() + mShadowHeight);
mShadowDrawable.draw(canvas);
}
canvas.restore();
}
}
//-- touch handling methods
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN
&& mTouchTarget == null
&& mPinnedSection != null
&& isPinnedViewTouched(mPinnedSection.view, x, y)) { // create touch target
// user touched pinned view
mTouchTarget = mPinnedSection.view;
mTouchPoint.x = x;
mTouchPoint.y = y;
// copy down event for eventually be used later
mDownEvent = MotionEvent.obtain(ev);
}
if (mTouchTarget != null) {
if (isPinnedViewTouched(mTouchTarget, x, y)) { // forward event to pinned view
mTouchTarget.dispatchTouchEvent(ev);
}
if (action == MotionEvent.ACTION_UP) { // perform onClick on pinned view
super.dispatchTouchEvent(ev);
performPinnedItemClick();
clearTouchTarget();
} else if (action == MotionEvent.ACTION_CANCEL) { // cancel
clearTouchTarget();
} else if (action == MotionEvent.ACTION_MOVE) {
if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {
// cancel sequence on touch target
MotionEvent event = MotionEvent.obtain(ev);
event.setAction(MotionEvent.ACTION_CANCEL);
mTouchTarget.dispatchTouchEvent(event);
event.recycle();
// provide correct sequence to super class for further handling
super.dispatchTouchEvent(mDownEvent);
super.dispatchTouchEvent(ev);
clearTouchTarget();
}
}
return true;
}
// call super if this was not our pinned view
return super.dispatchTouchEvent(ev);
}
private boolean isPinnedViewTouched(View view, float x, float y) {
view.getHitRect(mTouchRect);
// by taping top or bottom padding, the list performs on click on a border item.
// we don't add top padding here to keep behavior consistent.
mTouchRect.top += mTranslateY;
mTouchRect.bottom += mTranslateY + getPaddingTop();
mTouchRect.left += getPaddingLeft();
mTouchRect.right -= getPaddingRight();
return mTouchRect.contains((int)x, (int)y);
}
private void clearTouchTarget() {
mTouchTarget = null;
if (mDownEvent != null) {
mDownEvent.recycle();
mDownEvent = null;
}
}
private boolean performPinnedItemClick() {
if (mPinnedSection == null) return false;
OnItemClickListener listener = getOnItemClickListener();
if (listener != null && getAdapter().isEnabled(mPinnedSection.position)) {
View view = mPinnedSection.view;
playSoundEffect(SoundEffectConstants.CLICK);
if (view != null) {
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
}
listener.onItemClick(this, view, mPinnedSection.position, mPinnedSection.id);
return true;
}
return false;
}
public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {
if (adapter instanceof HeaderViewListAdapter) {
adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter();
}
return ((PinnedSectionListAdapter) adapter).isItemViewTypePinned(viewType);
}
}

Is it possible to make Imageview with checked and unchecked like radiobutton?

I am having nearly 500+ images in Imageview inside Horizontalscrollview. If i am selecting an image then I am marking it as selected. If I am selecting any other images in the view, it should be un-select and newly clicked image should have to be selected. How could I can achieve it?
for (int i = 0; i < Home.arr_category_item_list.size(); i++) {
ImageView circleImageView = new ImageView(getActivity());
imageLoader.get(Home.arr_category_item_list.get(i).get(Variables.EST_CATEGORY_ITEM_IMAGE), ImageLoader.getImageListener(circleImageView, R.drawable.defaultimage, R.drawable.defaultimage));
circleImageView.setTag(Integer.parseInt(Home.arr_category_item_list.get(i).get(Variables.EST_CATEGORY_ITEM_ID)));
circleImageView.setLayoutParams(params);
lnr_category_item.addView(circleImageView);
}
Please check the image attached. At the bottom of the screen there is an Image view. So user will have option to select only one image at a time.
Now this is some really old code, hope it still works.
Note: there may be a thing or two missing, but you can get the idea from this implementation
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import yourpackage.R;
//Creataed by Bojan Kseneman on 14.8.2013
public class CustomCheckBox extends ImageView {
private boolean isChecked;
private boolean isImageShown;
private boolean useCustomClickListener;
//private String android_xmlns = "http://schemas.android.com/apk/res/android";
private String app_xmlns;
private int checkboxOnResID;
private int checkboxOffResID;
private int checkboxDisabledOnResID;
private int checkboxDisabledOffResID;
//private int imageHeight;
//private int imageWidth;
public CustomCheckBox(Context context, AttributeSet attrs) {
super(context, attrs);
app_xmlns = new StringBuilder("http://schemas.android.com/apk/res/" + context.getPackageName()).toString();
init(attrs);
}
private void init(AttributeSet attrs) {
checkboxOnResID = attrs.getAttributeResourceValue(app_xmlns, "resourceChecked", R.drawable.round_checkbox_on);
checkboxOffResID = attrs.getAttributeResourceValue(app_xmlns, "resourceNotChecked", R.drawable.round_checkbox_off);
checkboxDisabledOnResID = attrs.getAttributeResourceValue(app_xmlns, "resourceDisabledOn", R.drawable.round_checkbox_off);
checkboxDisabledOffResID = attrs.getAttributeResourceValue(app_xmlns, "resourceDisabledOff", R.drawable.round_checkbox_off);
useCustomClickListener = attrs.getAttributeBooleanValue(app_xmlns, "customClickEvent", false);
if (useCustomClickListener)
this.setOnTouchListener(new CboxTouchListener());
else {
this.setOnTouchListener(new NormalClickListener());
}
if (!hasOnClickListener()) {
/**
* assign a new onClick listener so we get desired onClick sound
* effect (because we call it) this is opposite to how android
* behaves, where you don't hear the sound if there is not
* onClickListener assigned
*/
this.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
}
private boolean hasOnClickListener() {
try {
if (android.os.Build.VERSION.SDK_INT >= 14) {
//the information is inside ListenerInfo
java.lang.reflect.Field listenerInfoField = null;
listenerInfoField = Class.forName("android.view.View").getDeclaredField("mListenerInfo");
if (listenerInfoField != null)
listenerInfoField.setAccessible(true);
Object mOnClickListener = null;
mOnClickListener = listenerInfoField.get(this); //get from view object, in this case this is this
// get the field mOnClickListener, that holds the listener and cast it to a listener
java.lang.reflect.Field listenerField = null;
listenerField = Class.forName("android.view.View$ListenerInfo").getDeclaredField("mOnClickListener");
//View.OnClickListener myListener = (View.OnClickListener) listenerField.get(myLiObject);
return (listenerField.get(mOnClickListener) != null);
}
else {
//directly in View
java.lang.reflect.Field f = Class.forName("android.view.View").getDeclaredField("mOnClickListener");
return (f.get(this) != null);
}
}
catch (Exception e) {
return false;
}
}
// private void setScaledDownImage(int resID) {
// this.setImageBitmap(CommonMethods.decodeSampledBitmapFromResource(getContext(), resID, imageWidth, imageHeight));
// }
public boolean isChecked() {
return (isChecked && this.isEnabled());
}
public void setChecked(boolean isChecked) {
if (this.isEnabled())
setCheckedIgnoreEnabled(isChecked);
else
this.setEnabled(false);
}
public void setCheckedIgnoreEnabled(boolean isChecked) {
if ((this.isChecked != isChecked) || !isImageShown) {
this.isChecked = isChecked;
if (isChecked)
setImageResource(checkboxOnResID);
else
setImageResource(checkboxOffResID);
}
}
#Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (enabled)
setChecked(isChecked);
else {
int resID = isChecked ? checkboxDisabledOnResID : checkboxDisabledOffResID;
setImageResource(resID);
}
}
public void setCheckedAndEnabled(boolean isChecked, boolean isEnabled) {
setCheckedIgnoreEnabled(isChecked);
setEnabled(isEnabled);
}
public void toggle() {
setChecked(!isChecked);
}
public void toggleWithClick() {
toggle();
this.performClick();
}
public void toggleWithSilentClick() {
//v.playSoundEffect(android.view.SoundEffectConstants.CLICK);
boolean currentState = this.isSoundEffectsEnabled();
this.setSoundEffectsEnabled(false);
toggleWithClick();
this.setSoundEffectsEnabled(currentState);
}
private class CboxTouchListener extends ImageBoundClickListener {
#Override
public void doSomething() {
super.doSomething();
setChecked(!isChecked);
CustomCheckBox.this.performClick();
}
}
private class NormalClickListener extends MyOnClickListener {
#Override
public void doSomething() {
super.doSomething();
setChecked(!isChecked);
CustomCheckBox.this.performClick();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//this.imageWidth = MeasureSpec.getSize(widthMeasureSpec);
//this.imageHeight = MeasureSpec.getSize(heightMeasureSpec);
if (!isImageShown) {
setChecked(isChecked);
isImageShown = !isImageShown;
}
}
}
I used this click listener, since I was using some round images and I didn't want to trigger the click events when the user clicked on the transparent part of the image.
public abstract class ImageBoundClickListener implements android.view.View.OnTouchListener {
private String TAG = "ImageBoundsTouchListener";
private boolean shouldTriggerAction = false;
Rect allowedArea;
#Override
public boolean onTouch(View v, MotionEvent event) {
//let's us detect only clicks on the part where actual image is and not where the ImageView is
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float[] eventXY = new float[] { event.getX(), event.getY() };
allowedArea = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
android.graphics.Matrix invertMatrix = new android.graphics.Matrix();
ImageView iv = (ImageView) v;
iv.getImageMatrix().invert(invertMatrix);
invertMatrix.mapPoints(eventXY);
int x = (int) eventXY[0];
int y = (int) eventXY[1];
Drawable imgDrawable = iv.getDrawable();
Bitmap bitmap = ((BitmapDrawable) imgDrawable).getBitmap();
//Limit x,y within the bitmap
if (x < 0)
x = 0;
else if (x > (bitmap.getWidth() - 1))
x = bitmap.getWidth() - 1;
if (y < 0)
y = 0;
else if (y > bitmap.getHeight() - 1)
y = bitmap.getHeight() - 1;
int touchedRGB = bitmap.getPixel(x, y);
//is transparent?
shouldTriggerAction = (touchedRGB == 0) ? false : true;
return true;
case MotionEvent.ACTION_MOVE:
if (!allowedArea.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY()))
//the user went out of the user area
shouldTriggerAction = false;
return true;
case MotionEvent.ACTION_UP:
//finger is no longer on screen
if (shouldTriggerAction)
doSomething();
return true;
default:
return false;
}
}
public void doSomething() {
}
}

How to add animation while actionbar are changing their content with Appcompat?

I'm looking at Google's Material Design guidelines and I want add animated action bar. My goal is do something like this:
How can I add transition for action bar's content? I'm using Appcompat to keep backward compatibility.
Update:
I've created an open source library that provides transition/animation support to both View and MenuItem:
MenuItem transition
View transition
Instructions:
On Android Studio, add the code below to Gradle dependencies:
compile 'com.github.kaichunlin.transition:core:0.8.1'
Sample code with explanations:
protected void onCreate(Bundle savedInstanceState) {
//...
//standard onCreate() stuff that creates set configs toolbar, mDrawerLayout & mDrawerToggle
//Use the appropriate adapter that extends MenuBaseAdapter:
DrawerListenerAdapter mDrawerListenerAdapter = new DrawerListenerAdapter(mDrawerToggle, R.id.drawerList);
mDrawerListenerAdapter.setDrawerLayout(mDrawerLayout);
//Add desired transition to the adapter, MenuItemTransitionBuilder is used to build the transition:
//Creates a shared configuration that: applies alpha, the transition effect is applied in a cascading manner (v.s. simultaneously), MenuItems will resets to enabled when transiting, and invalidates menu on transition completion
MenuItemTransitionBuilder builder = MenuItemTransitionBuilder.transit(toolbar).alpha(1f, 0.5f).scale(1f, 0f).cascade(0.3f).visibleOnStartAnimation(true).invalidateOptionOnStopTransition(this, true);
MenuItemTransition mShrinkClose = builder.translationX(0, 30).build();
MenuItemTransition mShrinkOpen = builder.reverse().translationX(0, 30).build();
mDrawerListenerAdapter.setupOptions(this, new MenuOptionConfiguration(mShrinkOpen, R.menu.drawer), new MenuOptionConfiguration(mShrinkClose, R.menu.main));
}
//Let the adapter manage the creation of options menu:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
mDrawerListenerAdapter.onCreateOptionsMenu(this, menu);
return super.onCreateOptionsMenu(menu);
}
Source of the activity implementing the above is here, and a demo app here.
Originally Accepted Answer:
Here's a solution that's more versatile and is exactly how the MenuItem fade-out of Google Drive, Google Docs, Google Sheets, and Google Slides work.
The advantage is that when the user slide in from the left edge of the screen to open the drawer manually, or slide right when the drawer is opened to close it, the animation state is integrated with how the drawer is being opened/closed.
ProgressAnimator.java: This is the meat of the implementation, it translates a float based progression value (0f~1f) into a value that Android Animator understands.
public class ProgressAnimator implements TimeAnimator.TimeListener {
private final List<AnimationControl> animationControls = new ArrayList<>();
private final MenuItem mMenuItem; //TODO shouldn't be here, add animation end listener
private final ImageView mImageView;
private final TimeAnimator mTimeAnim;
private final AnimatorSet mInternalAnimSet;
public ProgressAnimator(Context context, MenuItem mMenuItem) {
if (mMenuItem == null) {
mImageView = null;
} else {
mImageView = (ImageView) LayoutInflater.from(context).inflate(R.layout.menu_animation, null).findViewById(R.id.menu_animation);
mImageView.setImageDrawable(mMenuItem.getIcon());
}
this.mMenuItem = mMenuItem;
this.mInternalAnimSet = new AnimatorSet();
mTimeAnim = new TimeAnimator();
mTimeAnim.setTimeListener(this);
}
public void addAnimatorSet(AnimatorSet mAnimSet, float start, float end) {
animationControls.add(new AnimationControl(mImageView, mAnimSet, start, end));
}
public void addAnimatorSet(Object target, AnimatorSet mAnimSet, float start, float end) {
animationControls.add(new AnimationControl(target, mAnimSet, start, end));
}
public void start() {
ValueAnimator colorAnim = ObjectAnimator.ofInt(new Object() {
private int dummy;
public int getDummy() {
return dummy;
}
public void setDummy(int dummy) {
this.dummy = dummy;
}
}, "dummy", 0, 1);
colorAnim.setDuration(Integer.MAX_VALUE);
mInternalAnimSet.play(colorAnim).with(mTimeAnim);
mInternalAnimSet.start();
if (mMenuItem != null) {
mMenuItem.setActionView(mImageView);
}
for (AnimationControl ctrl : animationControls) {
ctrl.start();
}
}
public void end() {
mTimeAnim.end();
if (mMenuItem != null) {
mMenuItem.setActionView(null);
}
}
public void updateProgress(float progress) {
for (AnimationControl ctrl : animationControls) {
ctrl.updateProgress(progress);
}
}
#Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
for (AnimationControl ctrl : animationControls) {
ctrl.updateState();
}
}
}
AnimationControl.java: Controls the progression of transition.
public class AnimationControl {
private AnimatorSet mAnimSet;
private Object target;
private float start;
private float end = 1.0f;
private float progressWidth;
private long time;
private boolean started;
private long mStartDelay;
private long mDuration;
private long mTotalDuration;
public AnimationControl(AnimatorSet mAnimSet, float start, float end) {
this(null, mAnimSet, start, end);
}
public AnimationControl(Object target, AnimatorSet mAnimSet, float start, float end) {
for (Animator animator : mAnimSet.getChildAnimations()) {
if (!(animator instanceof ValueAnimator)) {
throw new UnsupportedOperationException("Only ValueAnimator and its subclasses are supported");
}
}
this.target = target;
this.mAnimSet = mAnimSet;
mStartDelay = mAnimSet.getStartDelay();
mDuration = mAnimSet.getDuration();
if (mAnimSet.getDuration() >= 0) {
long duration = mAnimSet.getDuration();
for (Animator animator : mAnimSet.getChildAnimations()) {
animator.setDuration(duration);
}
} else {
for (Animator animator : mAnimSet.getChildAnimations()) {
long endTime = animator.getStartDelay() + animator.getDuration();
if (mDuration < endTime) {
mDuration = endTime;
}
}
}
mTotalDuration = mStartDelay + mDuration;
this.start = start;
this.end = end;
progressWidth = Math.abs(end - start);
}
public void start() {
if (target != null) {
for (Animator animator : mAnimSet.getChildAnimations()) {
animator.setTarget(target);
}
}
}
public void updateProgress(float progress) {
if (start < end && progress >= start && progress <= end || start > end && progress >= end && progress <= start) {
if (start < end) {
time = (long) (mTotalDuration * (progress - start) / progressWidth);
} else {
time = (long) (mTotalDuration - mTotalDuration * (progress - end) / progressWidth);
}
time -= mStartDelay;
if (time > 0) {
started = true;
}
Log.e(getClass().getSimpleName(), "updateState: " + mTotalDuration + ";" + time+"/"+start+"/"+end);
} else {
//forward
if (start < end) {
if (progress < start) {
time = 0;
} else if (progress > end) {
time = mTotalDuration;
}
//backward
} else if (start > end) {
if (progress > start) {
time = 0;
} else if (progress > end) {
time = mTotalDuration;
}
}
started = false;
}
}
public void updateState() {
if (started) {
for (Animator animator : mAnimSet.getChildAnimations()) {
ValueAnimator va = (ValueAnimator) animator;
long absTime = time - va.getStartDelay();
if (absTime > 0) {
va.setCurrentPlayTime(absTime);
}
}
}
}
}
ProgressDrawerListener.java: This listens for DrawerLayout state update and setup the required animation.
public class ProgressDrawerListener implements DrawerLayout.DrawerListener {
private final List<ProgressAnimator> mAnimatingMenuItems = new ArrayList<>();
private final Toolbar mToolbar;
private final ActionBarDrawerToggle mDrawerToggle;
private DrawerLayout.DrawerListener mDrawerListener;
private MenuItemAnimation mMenuItemAnimation;
private Animation mAnimation;
private boolean started;
private boolean mOpened;
private Activity mActivity;
private boolean mInvalidateOptionOnOpenClose;
public ProgressDrawerListener(Toolbar mToolbar, ActionBarDrawerToggle mDrawerToggle) {
this.mToolbar = mToolbar;
this.mDrawerToggle = mDrawerToggle;
}
#Override
public void onDrawerOpened(View view) {
mDrawerToggle.onDrawerOpened(view);
clearAnimation();
started = false;
if (mDrawerListener != null) {
mDrawerListener.onDrawerOpened(view);
}
mToolbar.getMenu().setGroupVisible(0, false); //TODO not always needed
mOpened=true;
mActivity.invalidateOptionsMenu();
}
#Override
public void onDrawerClosed(View view) {
mDrawerToggle.onDrawerClosed(view);
clearAnimation();
started = false;
if (mDrawerListener != null) {
mDrawerListener.onDrawerClosed(view);
}
mOpened=false;
mActivity.invalidateOptionsMenu();
}
#Override
public void onDrawerStateChanged(int state) {
mDrawerToggle.onDrawerStateChanged(state);
switch (state) {
case DrawerLayout.STATE_DRAGGING:
case DrawerLayout.STATE_SETTLING:
if (mAnimatingMenuItems.size() > 0 || started) {
break;
}
started = true;
setupAnimation();
break;
case DrawerLayout.STATE_IDLE:
clearAnimation();
started = false;
break;
}
if (mDrawerListener != null) {
mDrawerListener.onDrawerStateChanged(state);
}
}
private void setupAnimation() {
mToolbar.getMenu().setGroupVisible(0, true); //TODO not always needed
mAnimatingMenuItems.clear();
for (int i = 0; i < mToolbar.getChildCount(); i++) {
final View v = mToolbar.getChildAt(i);
if (v instanceof ActionMenuView) {
int menuItemCount = 0;
int childCount = ((ActionMenuView) v).getChildCount();
for (int j = 0; j < childCount; j++) {
if (((ActionMenuView) v).getChildAt(j) instanceof ActionMenuItemView) {
menuItemCount++;
}
}
for (int j = 0; j < childCount; j++) {
final View innerView = ((ActionMenuView) v).getChildAt(j);
if (innerView instanceof ActionMenuItemView) {
MenuItem mMenuItem = ((ActionMenuItemView) innerView).getItemData();
ProgressAnimator offsetAnimator = new ProgressAnimator(mToolbar.getContext(), mMenuItem);
if(mMenuItemAnimation!=null) {
mMenuItemAnimation.setupAnimation(mMenuItem, offsetAnimator, j, menuItemCount);
}
if(mAnimation!=null) {
mAnimation.setupAnimation(offsetAnimator);
}
offsetAnimator.start();
mAnimatingMenuItems.add(offsetAnimator);
}
}
}
}
onDrawerSlide(null, mOpened ? 1f : 0f);
Log.e(getClass().getSimpleName(), "setupAnimation: "+mAnimatingMenuItems.size()); //TODO
}
#Override
public void onDrawerSlide(View view, float slideOffset) {
for (ProgressAnimator ani : mAnimatingMenuItems) {
ani.updateProgress(slideOffset);
}
if(view==null) {
return;
}
mDrawerToggle.onDrawerSlide(view, slideOffset);
if (mDrawerListener != null) {
mDrawerListener.onDrawerSlide(view, slideOffset);
}
}
private void clearAnimation() {
for (ProgressAnimator ani : mAnimatingMenuItems) {
ani.end();
}
mAnimatingMenuItems.clear();
}
public void setDrawerListener(DrawerLayout.DrawerListener mDrawerListener) {
this.mDrawerListener = mDrawerListener;
}
public MenuItemAnimation getMenuItemAnimation() {
return mMenuItemAnimation;
}
public void setMenuItemAnimation(MenuItemAnimation mMenuItemAnimation) {
this.mMenuItemAnimation = mMenuItemAnimation;
}
public Animation getAnimation() {
return mAnimation;
}
public void setAnimation(Animation mAnimation) {
this.mAnimation = mAnimation;
}
public void setmInvalidateOptionOnOpenClose(Activity activity, boolean invalidateOptionOnOpenClose) {
mActivity=activity;
mInvalidateOptionOnOpenClose = invalidateOptionOnOpenClose;
}
public interface MenuItemAnimation {
public void setupAnimation(MenuItem mMenuItem, ProgressAnimator offsetAnimator, int itemIndex, int menuCount);
}
public interface Animation {
public void setupAnimation(ProgressAnimator offsetAnimator);
}
}
Set up in Activity: The example code below switches between two different menu options between opened and closed state. Optionally add offsetDrawerListener.setDrawerListener(DrawerListener) if you need to have your own DrawerListener.:
#Override
protected void onCreate(Bundle savedInstanceState) {
//other init
mProgressDrawerListener =new ProgressDrawerListener(toolbar, mDrawerToggle);
mProgressDrawerListener.setmInvalidateOptionOnOpenClose(this, true);
mOpenAnimation = new ProgressDrawerListener.MenuItemAnimation() {
#Override
public void setupAnimation(MenuItem mMenuItem, ProgressAnimator offsetAnimator, int itemIndex, int menuCount) {
MainActivity.this.setupAnimation(true, offsetAnimator, itemIndex);
}
};
mCloseAnimation = new ProgressDrawerListener.MenuItemAnimation() {
#Override
public void setupAnimation(MenuItem mMenuItem, ProgressAnimator offsetAnimator, int itemIndex, int menuCount) {
MainActivity.this.setupAnimation(false, offsetAnimator, itemIndex);
}
};
mDrawerLayout.setDrawerListener(mProgressDrawerListener);
}
//customize your animation here
private void setupAnimation(boolean open, ProgressAnimator offsetAnimator, int itemIndex) {
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(null, "alpha", 1.0f, 0f),
ObjectAnimator.ofFloat(null, "scaleX", 1.0f, 0f)
);
set.setStartDelay(itemIndex * 200);
set.setDuration(1000 - itemIndex * 200); //not the actual time the animation will be played
if(open) {
offsetAnimator.addAnimatorSet(set, 0, 1);
} else {
offsetAnimator.addAnimatorSet(set, 1, 0);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Only show items in the action bar relevant to this screen
// if the drawer is not showing. Otherwise, let the drawer
// decide what to show in the action bar.
if(mDrawerLayout.isDrawerOpen(findViewById(R.id.drawerList))) {
getMenuInflater().inflate(R.menu.drawer, menu);
mProgressDrawerListener.setMenuItemAnimation(
mCloseAnimation);
} else {
getMenuInflater().inflate(R.menu.main, menu);
mProgressDrawerListener.setMenuItemAnimation(
mOpenAnimation);
mDrawerLayout.setDrawerListener(mProgressDrawerListener);
}
return super.onCreateOptionsMenu(menu);
}
menu_animation.xml: This is to get the custom ActionView to have the same layout as the view used by MenuIem
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/menu_animation"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minWidth="#*android:dimen/action_button_min_width"
android:padding="8dp"
style="#style/Widget.AppCompat.ActionBar" />
I think I finally found your answer. This was way harder to find than I thought. If you take a look at this link: http://suhan.in/material-design-toolbar-animation/
The first one explains how this is done.
Below you find my own code snippet of how it could be done with only the menu items:
for(int i = 0; i < toolbarView.getChildCount(); i++)
{
final View v = toolbarView.getChildAt(i);
if(v instanceof ActionMenuView)
{
for(int j = 0; j < ((ActionMenuView)v).getChildCount(); j++)
{
final View innerView = ((ActionMenuView)v).getChildAt(j);
if(innerView instanceof ActionMenuItemView)
{
innerView.setTranslationY(-30);
innerView.animate().setStartDelay(100 + (j * 10)).setDuration(200).translationY(0);
}
}
}
}
This is the animation for the Y-axis. You can also add the animation for size, which I think they do in the design guidelines. Also if you don't want them to start simultaniously, you can set startDelay and add extra like this: setStartDelay(i * 10). This way each item starts the animation a little later. I already put this in the code snippet, but tweak it as how you would like it.

Fatal runtime error at android.media.audiofx.Equalizer.<init>

I am working on equalier app and no errors in code at all. when I run app on phone it force closes, I logcat and this is result
03-22 02:49:17.945: E/AndroidRuntime(4318): at android.media.audiofx.Equalizer.<init>(Equalizer.java:149)
03-22 02:50:34.796: E/AndroidRuntime(4423): at android.media.audiofx.Equalizer.<init>(Equalizer.java:149)
I tried on android gingerbread kitkat and jellybean
this is the main activity code:
public class MainActivity extends Activity
implements SeekBar.OnSeekBarChangeListener,
CompoundButton.OnCheckedChangeListener,
View.OnClickListener
{
//as usual "define variables"
TextView bass_boost_label = null;
SeekBar bass_boost = null;
CheckBox enabled = null;
Button flat = null;
Button Button1;
Equalizer eq = null;
BassBoost bb = null;
int min_level = 0;
int max_level = 100;
static final int MAX_SLIDERS = 8; // Must match the XML layout
SeekBar sliders[] = new SeekBar[MAX_SLIDERS];
TextView slider_labels[] = new TextView[MAX_SLIDERS];
int num_sliders = 0;
/*=============================================================================
onCreate
=============================================================================*/
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
/*=============================================================================
onCreate
=============================================================================*/
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//initalize layout
setContentView(R.layout.activity_main);
//initialize the checkbox
enabled = (CheckBox)findViewById(R.id.enabled);
enabled.setOnCheckedChangeListener (this);
//initialize flat button
flat = (Button)findViewById(R.id.flat);
flat.setOnClickListener(this);
Button1 = (Button)findViewById(R.id.button1);
Button1.setOnClickListener(this);
//the seekbars
bass_boost = (SeekBar)findViewById(R.id.bass_boost);
bass_boost.setOnSeekBarChangeListener(this);
bass_boost_label = (TextView) findViewById (R.id.bass_boost_label);
sliders[0] = (SeekBar)findViewById(R.id.slider_1);
slider_labels[0] = (TextView)findViewById(R.id.slider_label_1);
sliders[1] = (SeekBar)findViewById(R.id.slider_2);
slider_labels[1] = (TextView)findViewById(R.id.slider_label_2);
sliders[2] = (SeekBar)findViewById(R.id.slider_3);
slider_labels[2] = (TextView)findViewById(R.id.slider_label_3);
sliders[3] = (SeekBar)findViewById(R.id.slider_4);
slider_labels[3] = (TextView)findViewById(R.id.slider_label_4);
sliders[4] = (SeekBar)findViewById(R.id.slider_5);
slider_labels[4] = (TextView)findViewById(R.id.slider_label_5);
sliders[5] = (SeekBar)findViewById(R.id.slider_6);
slider_labels[5] = (TextView)findViewById(R.id.slider_label_6);
sliders[6] = (SeekBar)findViewById(R.id.slider_7);
slider_labels[6] = (TextView)findViewById(R.id.slider_label_7);
sliders[7] = (SeekBar)findViewById(R.id.slider_8);
slider_labels[7] = (TextView)findViewById(R.id.slider_label_8);
//define equilizer
eq = new Equalizer (0, 0);
if (eq != null)
{
eq.setEnabled (true);
int num_bands = eq.getNumberOfBands();
num_sliders = num_bands;
short r[] = eq.getBandLevelRange();
min_level = r[0];
max_level = r[1];
for (int i = 0; i < num_sliders && i < MAX_SLIDERS; i++)
{
int[] freq_range = eq.getBandFreqRange((short)i);
sliders[i].setOnSeekBarChangeListener(this);
slider_labels[i].setText (formatBandLabel (freq_range));
}
}
for (int i = num_sliders ; i < MAX_SLIDERS; i++)
{
sliders[i].setVisibility(View.GONE);
slider_labels[i].setVisibility(View.GONE);
}
bb = new BassBoost (0, 0);
if (bb != null)
{
}
else
{
bass_boost.setVisibility(View.GONE);
bass_boost_label.setVisibility(View.GONE);
}
updateUI();
}
/*=============================================================================
onProgressChanged
=============================================================================*/
#Override
public void onProgressChanged (SeekBar seekBar, int level,
boolean fromTouch)
{
if (seekBar == bass_boost)
{
bb.setEnabled (level > 0 ? true : false);
bb.setStrength ((short)level); // Already in the right range 0-1000
}
else if (eq != null)
{
int new_level = min_level + (max_level - min_level) * level / 100;
for (int i = 0; i < num_sliders; i++)
{
if (sliders[i] == seekBar)
{
eq.setBandLevel ((short)i, (short)new_level);
break;
}
}
}
}
/*=============================================================================
onStartTrackingTouch
=============================================================================*/
#Override
public void onStartTrackingTouch(SeekBar seekBar)
{
}
/*=============================================================================
onStopTrackingTouch
=============================================================================*/
#Override
public void onStopTrackingTouch(SeekBar seekBar)
{
}
/*=============================================================================
formatBandLabel
=============================================================================*/
public String formatBandLabel (int[] band)
{
return milliHzToString(band[0]) + "-" + milliHzToString(band[1]);
}
/*=============================================================================
milliHzToString
=============================================================================*/
public String milliHzToString (int milliHz)
{
if (milliHz < 1000) return "";
if (milliHz < 1000000)
return "" + (milliHz / 1000) + "Hz";
else
return "" + (milliHz / 1000000) + "kHz";
}
/*=============================================================================
updateSliders
=============================================================================*/
public void updateSliders ()
{
for (int i = 0; i < num_sliders; i++)
{
int level;
if (eq != null)
level = eq.getBandLevel ((short)i);
else
level = 0;
int pos = 100 * level / (max_level - min_level) + 50;
sliders[i].setProgress (pos);
}
}
/*=============================================================================
updateBassBoost
=============================================================================*/
public void updateBassBoost ()
{
if (bb != null)
bass_boost.setProgress (bb.getRoundedStrength());
else
bass_boost.setProgress (0);
}
/*=============================================================================
onCheckedChange
=============================================================================*/
#Override
public void onCheckedChanged (CompoundButton view, boolean isChecked)
{
if (view == (View) enabled)
{
eq.setEnabled (isChecked);
}
}
/*=============================================================================
onClick
=============================================================================*/
#Override
public void onClick (View view)
{
if (view == (View) flat)
{
setFlat();
}
if (view == (View) Button1)
{
Intent myIntent = new Intent(MainActivity.this, Guide.class);
MainActivity.this.startActivity(myIntent);
}
}
/*=============================================================================
updateUI
=============================================================================*/
public void updateUI ()
{
updateSliders();
updateBassBoost();
enabled.setChecked (eq.getEnabled());
}
/*=============================================================================
setFlat
=============================================================================*/
public void setFlat ()
{
if (eq != null)
{
for (int i = 0; i < num_sliders; i++)
{
eq.setBandLevel ((short)i, (short)0);
}
}
if (bb != null)
{
bb.setEnabled (false);
bb.setStrength ((short)0);
}
updateUI();
}
/*=============================================================================
showAbout
=============================================================================*/
public void showAbout ()
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle("About Simple EQ");
alertDialogBuilder.setMessage(R.string.copyright_message);
alertDialogBuilder.setCancelable(true);
alertDialogBuilder.setPositiveButton (R.string.ok,
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
}
});
AlertDialog ad = alertDialogBuilder.create();
ad.show();
}
/*=============================================================================
onOptionsItemSelected
=============================================================================*/
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.about:
showAbout();
return true;
}
return super.onOptionsItemSelected(item);
}
}

How to swipe zoomed image using ImageViews and PageAdapter

I am trying to create an image viewer which can load images from given URLs.The code below implement the User Interface.
My intention is to let user Zoom the image and move to next image with a Swipe event.But the problem is that when i zoom and then swipe , instead of showing the remaining portion , it moves to the next image.
I tried using requestDisallowInterceptTouchEvent in TouchImageView's(https://github.com/MikeOrtiz/TouchImageView) onTouchListener .After this the remaining portion could show but now i cannot go to the next page. I was wondering how this can be achieved as the event can only go to either TouchView or PageAdapter
public class PageActivity extends Activity {
private int numPages = 33;
private TouchImageView[] imageViews = new TouchImageView[numPages];
private String URL = "http://www.smbc-comics.com/comics/200905";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewPager viewPager = new ViewPager(this);
for (int i = 0; i < numPages; i++) {
imageViews[i] = new TouchImageView(this);
imageViews[i].setBackgroundResource(R.drawable.banke);
imageViews[i].setMaxZoom(4f);
}
setContentView(viewPager);
ImagePagerAdapter adapter = new ImagePagerAdapter();
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(2);
}
#SuppressWarnings("unused")
private class ImagePagerAdapter extends PagerAdapter {
#Override
public int getCount() {
return numPages;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((TouchImageView) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Context context = PageActivity.this;
String pageURL = URL;
if (imageViews[position].getDrawable() == null) {
ImageFetcher imagefetcher = new ImageFetcher();
imagefetcher.execute(
pageURL + String.format("%02d", position+1) + ".gif",
String.valueOf(position));
}
((ViewPager) container).addView(imageViews[position], 0);
return imageViews[position];
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((TouchImageView) object);
imageViews[position].setImageDrawable(null);
}
}
public class ImageFetcher extends AsyncTask<String, Integer, Drawable> {
int fillthisPos;
public Drawable doInBackground(String... urls) {
try {
InputStream is = (InputStream) new URL(urls[0]).getContent();
fillthisPos = Integer.parseInt(urls[1]);
Drawable d = Drawable.createFromStream(is, "src name");
return d;
} catch (Exception e) {
return null;
}
}
#Override
protected void onPostExecute(Drawable result) {
super.onPostExecute(result);
imageViews[fillthisPos].setImageDrawable(result);
result = null;
}
}
}
You can add following code in TouchImageView class:
public boolean isZoomed () {
return (normalizedScale > minScale);
}
private void onSwipeEvent(MotionEvent event) {
boolean zoomed = this.isZoomed();
if (!zoomed && (swipeLen > 0)) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
swipeStartPos = (int) event.getRawX();
}
else if (event.getAction() == MotionEvent.ACTION_MOVE) {
int distance = ((int) event.getRawX()) - swipeStartPos;
int swipeVector = SWIPE_RIGHT;
if (distance < 0) swipeVector = SWIPE_LEFT;
if (Math.abs(distance) > swipeLen) {
onSwipeHandler.onSwipe(swipeVector);
swipeStartPos = (int) event.getRawX();
this.setScaleX(1f);
}
else {
int swipeStDist = swipeLen - Math.round(((swipeLen / 100) * 50));
if (Math.abs(distance) > swipeStDist) {
this.setScaleX(0.98f);
}
}
}
else if (event.getAction() == MotionEvent.ACTION_UP) {
swipeStartPos = (int) event.getRawX();
this.setScaleX(1f);
}
}
}
public static int SWIPE_LEFT = 0;
public static int SWIPE_RIGHT = 1;
public int swipeStartPos = 0;
public onSwipeListener onSwipeHandler = new onSwipeListener() {
public void onSwipe(int vector) {}
};
public static int swipeLen = 0;
public void setOnSwipeListener(onSwipeListener c, int swipeLength) {
onSwipeHandler = c;
swipeLen = swipeLength;
}
public interface onSwipeListener {
public void onSwipe(int vector);
}
And also in TouchImageView below line 636 add onSwipeEvent(event) like this:
......
setImageMatrix(matrix);
onSwipeEvent(event);
//
// indicate event was handled
//
return true;
...........
After this from you code you can add swipe event listener, like this:
imageView.setOnSwipeListener(new TouchImageView.onSwipeListener() {
#Override
public void onSwipe(int vector) {
if (vector == TouchImageView.SWIPE_LEFT) {
Log.d("swipe", "swipe left!");
}
else if (vector == TouchImageView.SWIPE_RIGHT) {
Log.d("swipe", "swipe right!");
}
}
}, 200); //length of swiping - 200 dip
This onSwipeListener ignore onswipe where image is zoomed+. It work for me.

Categories