I downloaded the android-pinch jar so that I can have zoom functionality. The problem I am having is that I have to do setOnLongClickListener because I want to create a DragShadow for the user to see when they are dragging an icon or image and I want to assign a setOnClickListener to the image. However the PinchImageView for some reason doesn't work with my single and long click listeners, nothing happens.
Here is the code( it's a custom adapter ):
#Override
public View getView(int position, View view, ViewGroup viewGroup)
{
try
{
if(view == null)
view = myInflater.inflate(R.layout.tools_layout, viewGroup, false);
PinchImageView img = (PinchImageView)view.findViewById(R.id.imageView);
img.setBackgroundResource(drawId[position]);
img.setTag(icons.get(position));
img.setLongClickable(true);
img.setOnLongClickListener(longListen);
}
catch(Exception ex)
{
Log.i("customException", "getView():" + ex.getMessage());
}
return view;
}
View.OnLongClickListener longListen = new View.OnLongClickListener()
{
#Override
public boolean onLongClick(View v)
{
try
{
isListItem = true; // this never gets run...
Log.i(TAG, "long click");
ClipData data = ClipData.newPlainText("", "");
DragShadow dragShadow = new DragShadow(v);
v.startDrag(data, dragShadow, v, 0);
}
catch(Exception ex)
{
Log.i("customException", "longListen: " + ex.getMessage());
}
return false;
}
};
I've even tried changing the return to true because I saw in the TouchImageView that changing the return made it work, probably not the same for PinchImageView but was worth a try. Any suggestions?
You can try entering the PinchImageView source code and change the top to:
implements OnTouchListener, View.OnLongClickListener
Then implement a callback to your code or do whatever you want.
Related
I have fragment from which I'm launching activity with shared element transition that has viewpager in it, the enter transition works fine but when i scroll in view pager and finish transition the shared image comes from left side which is not desired it should reposition itself to where it was launched, here is my code:
Intent myIntent = new Intent(getActivity(), EnlargeActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat.
makeSceneTransitionAnimation(getActivity(),
imageView,
ViewCompat.getTransitionName(imageView));
startActivity(myIntent, options.toBundle());
I'm updating view and its name in activity that contains viewpager when finishing activity, but its going with blink:
public void finishAfterTransition() {
setEnterSharedElementCallback(new SharedElementCallback() {
#Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
// Clear all current shared views and names
names.clear();
sharedElements.clear();
ViewGroup viewGroup = (ViewGroup) viewPagerDetail.getAdapter()
.instantiateItem(viewPagerDetail, viewPagerDetail.getCurrentItem());
if (viewGroup == null) {
return;
}
// Map the first shared element name to the child ImageView.
sharedElements.put(viewGroup.findViewById(R.id.img).getTransitionName(), viewGroup.findViewById(R.id.img));
// setExitSharedElementCallback((SharedElementCallback) this);
}
});
super.finishAfterTransition();
Basically, Android start the transition with your pre-defined View and transitionName and automatically use the same properties for the return transition. When you change your focused View in ViewPager, Android doesn't know about that and keep the transition on the previous one on its way back. So you need to inform Android about the changes:
Remap the transition properties: Use setEnterSharedElementCallback to change the transitionName and View to the new one before returning from Activity2.
Wait for the Activity1 to finish rendering addOnPreDrawListener.
It's a bit complex in the final implementation. But you can look at my sample code https://github.com/tamhuynhit/PhotoGallery. I try to implement the shared-element-transition from many simple to complex sections.
Your problem appeared from Level 3 and solved in Level 4.
I am writing a tutorial about this but it's not in English so hope the code can help
UPDATE 1: Work flow
Here is how I implement it in my code:
Override finishAfterTransition in Activity2 and call setEnterSharedElementCallback method to re-map the current selected item in ViewPager. Also, call setResult to pass the new selected index back to previous activity here.
#Override
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void finishAfterTransition() {
setEnterSharedElementCallback(new SharedElementCallback() {
#Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
View selectedView = getSelectedView();
if (selectedView == null)
return;
// Clear all current shared views and names
names.clear();
sharedElements.clear();
// Store new selected view and name
String transitionName = ViewCompat.getTransitionName(selectedView);
names.add(transitionName);
sharedElements.put(transitionName, selectedView);
setExitSharedElementCallback((SharedElementCallback) null);
}
});
Intent intent = new Intent();
intent.putExtra(PHOTO_FOCUSED_INDEX, mCurrentIndex);
setResult(RESULT_PHOTO_CLOSED, intent);
super.finishAfterTransition();
}
Write a custom ShareElementCallback so I can set the callback before knowing which View is going to be used.
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static class CustomSharedElementCallback extends SharedElementCallback {
private View mView;
/**
* Set the transtion View to the callback, this should be called before starting the transition so the View is not null
*/
public void setView(View view) {
mView = view;
}
#Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
// Clear all current shared views and names
names.clear();
sharedElements.clear();
// Store new selected view and name
String transitionName = ViewCompat.getTransitionName(mView);
names.add(transitionName);
sharedElements.put(transitionName, mView);
}
}
Override onActivityReenter in Activity1, get the selected index from the result Intent. Set setExitSharedElementCallback to re-map new selected View when the transition begins.Call supportPostponeEnterTransition to delay a bit because your new View may not be rendered at this point. Use getViewTreeObserver().addOnPreDrawListener to listen for the layout changes, find the right View by the selected index and continue the transition supportStartPostponedEnterTransition.
#Override
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onActivityReenter(int resultCode, Intent data) {
if (resultCode != LevelFourFullPhotoActivity.RESULT_PHOTO_CLOSED || data == null)
return;
final int selectedIndex = data.getIntExtra(LevelFourFullPhotoActivity.PHOTO_FOCUSED_INDEX, -1);
if (selectedIndex == -1)
return;
// Scroll to the new selected view in case it's not currently visible on the screen
mPhotoList.scrollToPosition(selectedIndex);
final CustomSharedElementCallback callback = new CustomSharedElementCallback();
getActivity().setExitSharedElementCallback(callback);
// Listen for the transition end and clear all registered callback
getActivity().getWindow().getSharedElementExitTransition().addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) {}
#Override
public void onTransitionPause(Transition transition) {}
#Override
public void onTransitionResume(Transition transition) {}
#Override
public void onTransitionEnd(Transition transition) {
removeCallback();
}
#Override
public void onTransitionCancel(Transition transition) {
removeCallback();
}
private void removeCallback() {
if (getActivity() != null) {
getActivity().getWindow().getSharedElementExitTransition().removeListener(this);
getActivity().setExitSharedElementCallback((SharedElementCallback) null);
}
}
});
// Pause transition until the selected view is fully drawn
getActivity().supportPostponeEnterTransition();
// Listen for the RecyclerView pre draw to make sure the selected view is visible,
// and findViewHolderForAdapterPosition will return a non null ViewHolder
mPhotoList.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mPhotoList.getViewTreeObserver().removeOnPreDrawListener(this);
RecyclerView.ViewHolder holder = mPhotoList.findViewHolderForAdapterPosition(selectedIndex);
if (holder instanceof ViewHolder) {
callback.setView(((ViewHolder) holder).mPhotoImg);
}
// Continue the transition
getActivity().supportStartPostponedEnterTransition();
return true;
}
});
}
UPDATE 2: getSelectedItem
To get selected View from the ViewPager, don't use getChildAt or you get the wrong View, use findViewWithTag instead
In the PagerAdapter.instantiateItem, use position as tag for each View:
#Override
public View instantiateItem(ViewGroup container, int position) {
// Create the View
view.setTag(position)
// ...
}
Listen to onPageSelected event to get the selected index:
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
mSelectedIndex = position;
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
Call getSelectedView to get the current view by the selected index
private View getSelectedView() {
try {
return mPhotoViewPager.findViewWithTag(mSelectedIndex);
} catch (IndexOutOfBoundsException | NullPointerException ex) {
return null;
}
}
This is actually a default behavior, I was struggling SharedElementTransitions a lot, but I have nested fragments. I got my solution from an article (very recent article), it shows an implementation with a RecyclerView, which I assume you have. In short, the solution is to override onLayoutChange :
recyclerView.addOnLayoutChangeListener(
new OnLayoutChangeListener() {
#Override
public void onLayoutChange(View view,
int left,
int top,
int right,
int bottom,
int oldLeft,
int oldTop,
int oldRight,
int oldBottom) {
recyclerView.removeOnLayoutChangeListener(this);
final RecyclerView.LayoutManager layoutManager =
recyclerView.getLayoutManager();
View viewAtPosition =
layoutManager.findViewByPosition(MainActivity.currentPosition);
// Scroll to position if the view for the current position is null (not
// currently part of layout manager children), or it's not completely
// visible.
if (viewAtPosition == null
|| layoutManager.isViewPartiallyVisible(viewAtPosition, false, true)){
recyclerView.post(()
-> layoutManager.scrollToPosition(MainActivity.currentPosition));
}
}
});
Here is the article, and you will also find the project on GitHub.
I'm using FancyCoverFlow to show a CoverView in my Android App.
All works fine but the only think I can't do is overlap the items.
This is what I'm trying to do: preview
I tried using
coverFlow.setSpacing(-90);
or other configuration (such as -200 or -50), but setSpacing doesn't change anything in my final result.
This is the Adapter:
coverFlowAdapter = new FancyCoverFlowAdapter() {
#Override
public View getCoverFlowItem(int position, View reuseableView, ViewGroup viewGroup) {
v = reuseableView;
if (v == null) {
v = LayoutInflater.from(activity).inflate(R.layout.coverflo_item, viewGroup, false);
}
//do my stuff...
return v;
}
#Override
public int getCount() {
//do my stuff...
}
#Override
public Home getItem(int position) {
//do my stuff...
}
#Override
public long getItemId(int position) {
return 0;
}
};
These are coverFlow setting:
coverFlow.setAdapter(coverFlowAdapter);
coverFlow.setUnselectedSaturation(0.0f);
coverFlow.setSpacing(0);
coverFlow.setMaxRotation(0);
coverFlow.setScaleDownGravity(0.2f);
coverFlow.setReflectionEnabled(false);
coverFlow.setUnselectedAlpha(1.0f);
coverFlow.setActionDistance(FancyCoverFlow.ACTION_DISTANCE_AUTO);
coverFlow.setUnselectedScale(0.75f);
There is another StackOverflow question about this issue, but is not useful:
Android Cover Flow gallery and remove space between
I tried also other sources, but I found only coverFlow libraries that use image as item, while I need to use my custom layout.
Thanks.
In my main activity I display a ListView which uses a custom BaseAdapter (ThoughtListAdapter).
listView = (ListView) findViewById(R.id.list);
adapter = new ThoughtListAdapter(this, resultingThoughts);
listView.setAdapter(adapter);
Every item in the ListView has a custom layout containing a TextView and two Button.
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.list_item_thought, null);
}
thoughtText = (TextView) convertView.findViewById(R.id.thought_text_view);
likeButton = (Button) convertView.findViewById(R.id.thought_like_button);
dislikeButton = (Button) convertView.findViewById(R.id.thought_dislike_button);
When a Button is clicked an AsyncTask (AsyncPost) is called which connects to my database and makes some changes.
likeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
System.out.println("LIKE CLICKED");
Thought t = thoughtItems.get(position);
thoughtId = t.getId();
opinion = 1;
AsyncPost asyncPost = new AsyncPost(activity,ThoughtListAdapter.this);
asyncPost.execute(SHARE_THOUGHT_URL,
TAG_PERSON_EMAIL, "m#b.it",
TAG_THOUGHT_ID, thoughtId.toString(),
TAG_OPINION, opinion.toString());
}
});
What I need is making both Button-s of a list item disappear after the AsyncTask is done with a successful outcome. I have a method onComplete(JSONObject json) which elaborates the JSONObject returned by the AsyncTask. I try to make the buttons non visible inside the onComplete method, but this doesn't work because onComplete() doesn't know which exact button has been clicked.
How can I pass an instance of the exact clicked button inside onComplete() and make disappear only the Like and Dislike buttons of the concerned list item?
AsyncPost is a global AsyncTask used by all my other activities. I would strongly prefer to leave it alone. The onComplete() method functions as the onPostExecute() method of the AsyncTask.
Here are the getView() and onComplete() methods inside my BaseAdapter, which contain all the code shown above.
Thank you.
public View getView(final int position, View convertView, ViewGroup parent) {
if (layoutInflater == null) {
layoutInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.list_item_thought, null);
}
thoughtText = (TextView) convertView.findViewById(R.id.thought_text_view);
likeButton = (Button) convertView.findViewById(R.id.thought_like_button);
dislikeButton = (Button) convertView.findViewById(R.id.thought_dislike_button);
//thoughtItems is a list of custom ojbects (Thought)
Thought t = thoughtItems.get(position);
//Here i set the content of the current TextView
thoughtText.setText(t.getText());
//the two buttons do basically the same thing when get clicked
likeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Thought t = thoughtItems.get(position);
thoughtId = t.getId();
opinion = 1;
AsyncPost asyncPost = new AsyncPost(activity,ThoughtListAdapter.this);
asyncPost.execute(SHARE_THOUGHT_URL,
TAG_PERSON_EMAIL, "m#b.it",
TAG_THOUGHT_ID, thoughtId.toString(),
TAG_OPINION, opinion.toString());
}
});
dislikeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Thought t = thoughtItems.get(position);
thoughtId = t.getId();
opinion = 0;
AsyncPost asyncPost = new AsyncPost(activity,ThoughtListAdapter.this);
asyncPost.execute(SHARE_THOUGHT_URL,
TAG_PERSON_EMAIL, "m#b.it",
TAG_THOUGHT_ID, thoughtId.toString(),
TAG_OPINION, opinion.toString());
}
});
return convertView;
}
#Override
public void onComplete(JSONObject json) {
if (json != null) {
try {
if (json.getInt(TAG_SUCCESS) == 0) {
Toast.makeText(activity, "Operazione non riuscita.", Toast.LENGTH_LONG).show();
} else {
//if everything is good i try to make the buttons of that particular list item disappear
likeButton.setVisibility(View.GONE);
dislikeButton.setVisibility(View.GONE);
}
}
catch (JSONException e) {
Log.e(TAG_LOG, "JSONException", e);
}
}
else Toast.makeText(activity, "Errore connessione!", Toast.LENGTH_LONG).show();
}
One solution to this would be to have something on your Thought object to indicate whether or not to show the buttons.
So in your getView() method you check this
likeButton = (Button) convertView.findViewById(R.id.thought_like_button);
dislikeButton = (Button) convertView.findViewById(R.id.thought_dislike_button);
Thought t = thoughtItems.get(position);
if (t.hideButtons()) {
likeButton.setVisibility(View.GONE);
dislikeButton.setVisibility(View.GONE);
}
else {
likeButton.setVisibility(View.VISIBLE);
dislikeButton.setVisibility(View.VISIBLE);
}
Then you would need to have your onComplete method return the id of the Thought object that it related to. Then inside your onComplete you could do
int id = //get your id from your JSON response
for(Thought t : thoughtItems) {
if (t.getId() == id) {
t.setHideButtons(true);
notifyDataSetChanged();
break;
}
}
By calling notifyDataSetChanged() it will redraw your list and when it does the check for whether it should show the buttons or not it will not show them because it was set on that thought item
As a french developper, I apologize about my English.
So, my aim is to create a phone book for my android application. This phone book is made of a ListView. I've populated my ListView with a custom adapter which allows the user to select some TextViews in each rows.
I presume a picture is better than thousand words, so here it is:
As you can see the red parts are the TextViews that I have selected.
The issue I have to face is the following:
When I select a TextView from a row, the row that is 4 places under has its TextView that gets selected too! Is that a common issue or is it due to my code?
I've added a log on the click listener, for each TextView I click, I receive only one log, so I don't think the problem comes from my work.
For example and if some of you don't understand what I said; I select the TextView with the drawableLeft picture in the second row. The logcat returns me the following entry:
"Select: 1" (as defined in my code).
If I scroll down my ListView, I can see that my second row (ie. my TextView) is selected as expected but also my 6th row, which is not expected at all!
Here is the code I use in order to color or not a row:
public View getView(final int position, View convertView, ViewGroup parent)
{
selectedLocations.add(new Boolean(false));
selectedAvailabilities.add(new Boolean(false));
if (convertView == null) convertView = inflater.inflate(R.layout.phonebook_adapter_layout, parent, false);
final LinearLayout root = (LinearLayout) convertView.findViewById(R.id.phonebook_adapter_root);
final ImageView photo = (ImageView) convertView.findViewById(R.id.phonebook_adapter_image);
final TextView firstname = (TextView) convertView.findViewById(R.id.phonebook_adapter_firstname);
final TextView lastname = (TextView) convertView.findViewById(R.id.phonebook_adapter_lastname);
final TextView location = (TextView) convertView.findViewById(R.id.phonebook_adapter_location);
final TextView availability = (TextView) convertView.findViewById(R.id.phonebook_adapter_availability);
Bitmap mBitmap = null;
try
{
mBitmap = Media.getBitmap(context.getContentResolver(), Uri.parse(relations.get(position).getPhoto()));
photo.setImageBitmap(mBitmap);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
firstname.setText(relations.get(position).getFirstName());
lastname.setText(relations.get(position).getLastName());
DBStatus dbStatus = new DBStatus(KramerApplication.getInstance());
Status status = dbStatus.getWithRelation(relations.get(position));
dbStatus.close();
if (status != null)
{
location.setText( status.getLocation() );
availability.setText( status.getAvailability() );
if ( status.getDisplayedAvailability(2).equals("Busy") )
availability.setCompoundDrawablesWithIntrinsicBounds(R.drawable.availability_busy, 0, 0, 0);
else if ( status.getDisplayedAvailability(2).equals("Occupied") )
availability.setCompoundDrawablesWithIntrinsicBounds(R.drawable.availability_busy, 0, 0, 0);
else if ( status.getDisplayedAvailability(2).equals("Free") )
availability.setCompoundDrawablesWithIntrinsicBounds(R.drawable.availability_on, 0, 0, 0);
else
availability.setCompoundDrawablesWithIntrinsicBounds(R.drawable.availability_off, 0, 0, 0);
}
root.setOnClickListener(new OnClickListener() {
public void onClick(View v)
{
Intent intent = new Intent(context, ContactDetailsActivity.class);
intent.putExtra("contact_id", relations.get(position).getId());
context.startActivity(intent);
}
});
location.setOnClickListener(new OnClickListener() {
public void onClick(View v)
{
if (selectedLocations.get(position).booleanValue())
{
selectedLocations.set(position, new Boolean(false));
location.setBackgroundColor(Color.TRANSPARENT);
}
else
{
selectedLocations.set(position, new Boolean(true));
location.setBackgroundColor(Color.RED);
}
}
});
availability.setOnClickListener(new OnClickListener() {
public void onClick(View v)
{
if (selectedAvailabilities.get(position).booleanValue())
{
selectedAvailabilities.set(position, new Boolean(false));
availability.setBackgroundColor(Color.TRANSPARENT);
}
else
{
selectedAvailabilities.set(position, new Boolean(true));
availability.setBackgroundColor(Color.RED);
}
}
});
return convertView;
}
The ArrayLists "selectedAvailabilities" and "selectedLocations" have been properly initialized in the constructor and do their jobs when I use them in another activity (Read only).
I hope you'll be able to help me.
Regards.
V.
################################# SOLUTION
If someone is looking for the solution, here it is. Many thanks to user936414!.
Replace (line 6):
if (convertView == null) convertView = inflater.inflate(R.layout.phonebook_adapter_layout, parent, false);
By:
convertView = inflater.inflate(R.layout.phonebook_adapter_layout, parent, false);
Before:
return convertView;
Add:
if (selectedAvailabilities.get(position).booleanValue())
{
availability.setBackgroundColor(Color.RED);
}
if (selectedLocations.get(position).booleanValue())
{
location.setBackgroundColor(Color.RED);
}
The reason for this behavior is the use of convertview. To solve this, have a HashSet and put all the positions selected in the HashSet. In getView check contains of Hashset and setSelected for the TextView. Hope this helps.
Add
if (selectedLocations.get(position).booleanValue())
{
location.setBackgroundColor(Color.RED);
}
else
{
location.setBackgroundColor(Color.TRANSPARENT);
}
outside the onClickListener also to ensure convertView does not affect your requirement.
i'm trying to set a child click listener on my ExpandedListView, and i'm getting some strange results:
theList.setOnChildClickListener(new OnChildClickListener() {
#Override
public boolean onChildClick(ExpandableListView parent, View view, int groupPosition, int childPosition, long id) {
Log.i(LOG_TAG, "clicked");
ExpandableListAdapter adapter = parent.getExpandableListAdapter();
View v = (View)adapter.getChild(groupPosition, childPosition);
Toast toast = Toast.makeText(getApplicationContext(),
"item: " + new Integer(groupPosition).toString() + ":" + new Integer(childPosition).toString(),
Toast.LENGTH_SHORT);
toast.show();
if (!((CheckedTextView)view.findViewById(R.id.check)).isChecked()){
((CheckedTextView)view.findViewById(R.id.check)).setChecked(true);
} else {
((CheckedTextView)view.findViewById(R.id.check)).setChecked(false);
}
return true;
}
});
it sort of works. when i check one of the items, however, another, additional item will also be selected, seemingly at random. am i not doin this correctly?
Solved the issue myself. i ended up extending the adapter class and overriding getChildView and getGroupView and added a list of booleans to the adapter to keep up with the checkmarks. probably not the most sophisticated solution, but works perfectly.