I am developing a keyboard for android. Now I want to change my keyboard from the letters to symbols then the user press the '123' Button.
My Poblem is, that the keyboard overflows after calling keyboardView.setKeyboard().
What I tried:
I logged the width of the keyboards. the normal width is 720, after setting the new keyboard, it is over 800.
I also tried to call keyboardView.invalidate() and invalidateAllKEeys(). Both had no effect.
I copiy-pasted the keyboard XML layout to exclude issues caused by the layout.
How cat I fix that?
Thank you for helping me.
Normal keyboard
keyboard after setting new keyboard
Here my Input Service class
package com.luxaaa.keyboardtest.services;
#SuppressWarnings("deprecation")
public class MyInputMethodService extends InputMethodService implements
KeyboardView.OnKeyboardActionListener {
private KeyboardView keyboardView;
private Keyboard keyboard, keyBoardSymbolic;
private boolean caps = false;
Vibrator vibrator;
VibrationEffect vEffect;
public MyInputMethodService() {
super();
}
#Override
public View onCreateInputView() {
LinearLayout layout =new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
View view = getLayoutInflater().inflate(R.layout.keyboard_bar, null);
// Keyboards
keyboard =new Keyboard(this, R.xml.keys_layout);
keyBoardSymbolic =new Keyboard(this, R.xml.keys_layout);
keyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.keyboard_view, null);
keyboardView.setKeyboard(keyBoardSymbolic);
keyboardView.setOnKeyboardActionListener(this);
keyboardView.setPreviewEnabled(false);
layout.addView(view);
layout.addView(keyboardView);
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
return layout;
}
#Override
public void onPress(int primaryCode) {
if(primaryCode != KeyEvent.KEYCODE_SPACE) {
keyboardView.setPreviewEnabled(true);
}
// key feedback
vibrator.vibrate(50);
// set shift keys pressed if caps
if(primaryCode == Keyboard.KEYCODE_SHIFT) {
List<Keyboard.Key> keys = keyboard.getKeys();
List<Keyboard.Key> keysSymbolic = keyBoardSymbolic.getKeys();
for(Keyboard.Key key : keys) {
if(key.codes[0] == primaryCode) {
key.pressed = !caps;
}
}
for(Keyboard.Key key : keysSymbolic) {
if(key.codes[0] == primaryCode) {
key.pressed = !caps;
}
}
}
}
#Override
public void onRelease(int primaryCode) {
keyboardView.setPreviewEnabled(false);
}
#Override
public void onKey(int primaryCode, int[] keyCodes) {
InputConnection inputConnection = getCurrentInputConnection();
if (inputConnection != null) {
switch (primaryCode) {
case Keyboard.KEYCODE_DELETE :
CharSequence selectedText = inputConnection.getSelectedText(0);
if(TextUtils.isEmpty(selectedText)) {
inputConnection.deleteSurroundingText(1,0);
} else {
inputConnection.commitText("", 1);
}
break;
case Keyboard.KEYCODE_SHIFT :
caps = !caps;
keyboard.setShifted(caps);
keyboardView.invalidateAllKeys();
break;
case Keyboard.KEYCODE_DONE :
inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
break;
case -10 :
keyboardView.setKeyboard(keyboard);
break;
default:
char code = (char) primaryCode;
if(Character.isLetter(code) && caps) {
code = Character.toUpperCase(code);
}
inputConnection.commitText(String.valueOf(code), 1);
break;
}
}
}
#Override
public void onText(CharSequence text) {
}
#Override
public void swipeLeft() {
}
#Override
public void swipeRight() {
}
#Override
public void swipeDown() {
}
#Override
public void swipeUp() {
}
}
Related
I am new to Java coding for android. I need to understand why does my code generate 2 threads. The problem that can be created over here is perhaps competition for Camera Resources which results in the camera not being used by the user. Kindly suggest a solution to my problem as well.
I have also attached a picture where there are 2 requests for a new activity. There is also proof by having 2 thread IDs active.
EDIT for clarity:
I want to generate a new thread which solely handles the activity to record the video, no other thread should be doing it. But there are two that are performing their own activity to record video.
public class MainActivity extends AppCompatActivity implements SensorEventListener/*, ActivityCompat.OnRequestPermissionsResultCallback*/ {
private Camera c;
private MainActivity reference_this = this;
private BooleanObject recorded_video = new BooleanObject("recorded_video",false);
private BooleanObject recorded_motions_chest = new BooleanObject("recorded_motions_chest", false);
private ChangeListener listener_change;
public void setListener(ChangeListener arg_listener_change) {
listener_change = arg_listener_change;
}
public interface ChangeListener {
void onChange(String arg_name);
}
public void set_recorded_video(boolean arg_recorded_video) {
recorded_video.set_value(arg_recorded_video);
if (listener_change != null) { listener_change.onChange(recorded_video.get_name()); }
}
public void set_recorded_motions_chest(boolean arg_recorded_motions_chest) {
recorded_motions_chest.set_value(arg_recorded_motions_chest);
if (listener_change != null) { listener_change.onChange(recorded_motions_chest.get_name()); }
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (!(getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA))) {
this.finish();
System.exit(0);
}
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
this.setListener(new MainActivity.ChangeListener() {
#Override
public void onChange(String arg_name) {
Log.i("CHNG", "fired");
if (arg_name.equals(recorded_video.get_name())) {
VideoCapture video_capture = new VideoCapture();
video_capture.open(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/video_finger.mp4");
Mat frame = new Mat();
int length_video = (int) video_capture.get(Videoio.CAP_PROP_FRAME_COUNT);
int rate_frame = (int) video_capture.get(Videoio.CAP_PROP_FPS);
while (true) {
video_capture.read(frame);
Size size_img = frame.size();
Log.i("MAT", String.valueOf(size_img.width) + ' ' + String.valueOf(size_img.height));
}
}
else if (arg_name.equals(recorded_motions_chest.get_name())) {}
}
});
// new Thread(new Runnable() {
// public void run() {
Button button_symptoms = (Button) findViewById(R.id.button_symptoms);
Button button_upload_signs = (Button) findViewById(R.id.button_upload_signs);
Button button_measure_heart_rate = (Button) findViewById(R.id.button_measure_heart_rate);
Button button_measure_respiratory_rate = (Button) findViewById(R.id.button_measure_respiratory_rate);
CameraView cv1 = new CameraView(getApplicationContext(), reference_this);
FrameLayout view_camera = (FrameLayout) findViewById(R.id.view_camera);
view_camera.addView(cv1);
TextView finger_on_sensor = (TextView) findViewById(R.id.text_finger_on_sensor);
finger_on_sensor.setVisibility(View.INVISIBLE);
finger_on_sensor.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch (View arg_view, MotionEvent arg_me){
finger_on_sensor.setVisibility(View.INVISIBLE);
/*GENERATING THREADS OVER HERE*/ new Thread(new Runnable() {
public void run() {
File file_video = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/video_finger.mp4");
// final int REQUEST_WRITE_PERMISSION = 786;
final int VIDEO_CAPTURE = 1;
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
Intent intent_record_video = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent_record_video.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 45);
Uri fileUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cse535a1.provider", file_video);
intent_record_video.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
if (intent_record_video.resolveActivity(getPackageManager()) != null) {
Log.i("CAM", "CAM requeted");
Log.i("Thread ID", String.valueOf(Thread.currentThread().getId()));
startActivityForResult(intent_record_video, VIDEO_CAPTURE);
// reference_this.set_recorded_video(true);
}
}
}).start();
return true;
}
});
button_symptoms.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick (View arg_view){
Intent intent = new Intent(getApplicationContext(), Loggin_symptoms.class);
startActivity(intent);
}
});
button_upload_signs.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick (View arg_view){
}
});
button_measure_heart_rate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick (View arg_view){ finger_on_sensor.setVisibility(View.VISIBLE); }
});
button_measure_respiratory_rate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg_view) {
SensorManager manager_sensor = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor sensor_accelerometer = manager_sensor.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
manager_sensor.registerListener(MainActivity.this, sensor_accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
});
// }}).start();
}
public void setCam(Camera arg_camera) { c = arg_camera; }
#Override
public void onSensorChanged(SensorEvent arg_event) {
float x = arg_event.values[0];
float y = arg_event.values[1];
float z = arg_event.values[2];
Log.i("ACCELEROMETER", String.valueOf(x) + ' ' + String.valueOf(y) + ' ' + String.valueOf(z));
}
#Override
public void onAccuracyChanged(Sensor arg_sensor, int arg_accuracy) {
}
#Override
protected void onPause() {
super.onPause();
c.unlock();
// Log.i("CAM", "unlocked");
// if (c != null) {
// c.stopPreview();
//// c.release();
//// c = null;
// }
}
#Override
protected void onResume() {
super.onResume();
if (c != null) c.lock();
// if (c != null) {
// c.stopPreview();
// c.release();
// c = null;
/// }
// cv1 = new CameraView(getApplicationContext(), this);
// view_camera.addView(cv1);
}
#Override
protected void onDestroy() {
if (c != null) {
// c.unlock();
c.stopPreview();
c.release();
c = null;
}
super.onDestroy();
}
private static class BooleanObject {
private String name = "BooleanObject";
private boolean value = false;
public BooleanObject(String arg_name, boolean arg_value) {
name = arg_name;
value = arg_value;
}
public String set_name(String arg_name) {
name = arg_name;
return name;
}
public boolean set_value(boolean arg_value) {
value = arg_value;
return value;
}
public String get_name() { return name; }
public boolean get_value() { return value; }
};
}
Every time you tap on TextView, as you are using onTouchListener, it will be fired twice, once for ACTION_DOWN and ACTION_UP, so check for ACTION_UP as in
finger_on_sensor.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch (View arg_view, MotionEvent arg_me){
if(arg_me.getAction() == MotionEvent.ACTION_UP){ //add this condition
finger_on_sensor.setVisibility(View.INVISIBLE);
new Thread(new Runnable() {
public void run() {
File file_video = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/video_finger.mp4");
// final int REQUEST_WRITE_PERMISSION = 786;
final int VIDEO_CAPTURE = 1;
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
Intent intent_record_video = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent_record_video.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 45);
Uri fileUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cse535a1.provider", file_video);
intent_record_video.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
if (intent_record_video.resolveActivity(getPackageManager()) != null) {
Log.i("CAM", "CAM requeted");
Log.i("Thread ID", String.valueOf(Thread.currentThread().getId()));
startActivityForResult(intent_record_video, VIDEO_CAPTURE);
//reference_this.set_recorded_video(true);
}
}
}).start();
return true;
}
});
I'm from iOS development and new in Android app making, something looks really strange to me in Android, why EditText stay focused when keyboard is hidden ??
I've tried to set a OnFocusChangeListener on my EditText but this is not working when the keyboard hide, the listener isn't called.
I've also tried to detect keyboard hiding with a onChangeListener but it doesn't work.. (only with hard keyboard apparently).
#Override
public void onFocusChange(View v, boolean hasFocus) {
// not called when keyboard hides
}
});
I've been looking for a while and I only found answer for stopping focus at first launch but that's not what I'm looking for..
Thanks
Okay, kind of this
private void setUpEtxFocusListener() {
etx.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean hasFocus) {
if (hasFocus) {
if ((LOG_DEBUG)) Log.d(TAG, "etx : GOT Focus");
} else {
if ((LOG_DEBUG)) Log.d(TAG, "etx : LOST Focus");
}
}
});
and when clicked outside, etx will lose focus and you hide the keyboard
//this will trigger etx setOnFocusChangeListener - onFocus change() - NO FOCUS clause
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if (v instanceof EditText) {
Rect outRect = new Rect();
v.getGlobalVisibleRect(outRect);
if (!outRect.contains((int) event.getRawX(), (int) event.getRawY())) {
v.clearFocus();
// just an utility class to hide.
UtilExtra.hideKeyboard(this);
}
}
}
return super.dispatchTouchEvent(event);
}
Try this:
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
you can simply use this code and your focus will be gone
editText.clearFocus();
To catch keyboard's open/close event use this code:
//...
private int layoutSize = 0;
private boolean isKeyboardVisible = false;
//...
#Override
public void onCreate(Bundle savedInstanceState) {
setKeyboardOpenListener();
}
//...
private void setKeyboardOpenListener() {
View activityRootView = findViewById(android.R.id.content);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
if (activityRootView.getHeight() + Util.getStatusBarHeight(this) >= layoutSize) {
layoutSize = activityRootView.getHeight();
if (isKeyboardVisible) {
isKeyboardVisible = false;
onKeyboardClose();
}
} else {
if (!isKeyboardVisible) {
isKeyboardVisible = true;
onKeyboardOpen();
}
}
});
}
//...
public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
Use editText.clearFocus() and editText.setCursorVisible(false) both methods, It may helps you.
I want to hide a WebView object (txtCode) if the code property of a custom object Arraylist (arrQues) contains nothing.
if (arrQues.get(count).code.isEmpty())
txtCode.setVisibility(View.GONE);
Its an ArrayList of custom objects fetched from a database table which is shown below
And if the code property does contains code then I have dynamically added rules to layout as shown below:
if (!(arrQues.get(count).code.isEmpty())) {
submit_params.removeRule(RelativeLayout.BELOW);
submit_params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
submit_params.bottomMargin = (int) convertPxToDp(getContext(), convertDpToPx(getContext(), 15));
main_params.addRule(RelativeLayout.ABOVE, submitContainer.getId());
mainContainer.setLayoutParams(main_params);
submitContainer.setLayoutParams(submit_params);
}
The issue is when I load the second question and so on... the layout gets messed up and the current question number does not shows as 2 even if its 2 as shown in below:
Both of these issues only arises whenever I use...
arrQues.get(count).code.isEmpty() in the code
I have also tried using "" instead of isEmpty() and even null, but the result was same.
Also what I have noticed is only those questions are loaded from database which have something in the code column.
Below is the complete code for Java file
public class QuestionsFragment extends Fragment implements View.OnClickListener {
TextView txtTimer, txtStatus;
LinearLayout boxA, boxB, boxC, boxD, mainContainer;
RelativeLayout submitContainer;
RelativeLayout.LayoutParams submit_params;
RelativeLayout.LayoutParams main_params;
ScrollView scrollView;
Button btnSubmit;
DBHelper dbHelper;
SharedPreferences sharedPreferences;
TextView txtQues;
WebView txtCode;
TextView txtOptA, txtOptB, txtOptC, txtOptD;
String ans;
ArrayList<QuestionModal> arrQues = new ArrayList<>();
ArrayList<String> arrAnswers = new ArrayList<>();
CountDownTimer countDownTimer;
boolean timerSwitch;
int selectedVal, id;
int curr_quesNo = 0;
int count = 0;
int right = 0;
int non_attempted = 0;
public QuestionsFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_questions, container, false);
txtQues = view.findViewById(R.id.txtQues);
txtOptA = view.findViewById(R.id.txtOptionA);
txtOptB = view.findViewById(R.id.txtOptionB);
txtOptC = view.findViewById(R.id.txtOptionC);
txtOptD = view.findViewById(R.id.txtOptionD);
txtCode = view.findViewById(R.id.txtCode);
txtStatus = view.findViewById(R.id.txtStatus);
boxA = view.findViewById(R.id.boxA);
boxB = view.findViewById(R.id.boxB);
boxC = view.findViewById(R.id.boxC);
boxD = view.findViewById(R.id.boxD);
scrollView = view.findViewById(R.id.scrollView);
btnSubmit = view.findViewById(R.id.btnSubmit);
submitContainer = view.findViewById(R.id.submitContainer);
mainContainer = view.findViewById(R.id.mainContainer);
submit_params = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
main_params = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
sharedPreferences = getActivity().getSharedPreferences("PrefFile", MODE_PRIVATE);
timerSwitch = sharedPreferences.getBoolean("timer_switch", true);
selectedVal = sharedPreferences.getInt("selectedVal", 10);
dbHelper = DBHelper.getDB(getActivity(), sharedPreferences.getString("db_name", null));
if (!dbHelper.checkDB()) {
dbHelper.createDB(getActivity());
}
dbHelper.openDB();
String levelKey = sharedPreferences.getString("level_key", null);
arrQues = dbHelper.getQues(levelKey, selectedVal);
loadQues(timerSwitch);
txtTimer = view.findViewById(R.id.txtTimer);
switch (sharedPreferences.getString("db_name", null)) {
case "Android":
((MainActivity) getActivity()).setFragTitle("Android Quiz");
// topicLogo.setImageResource(R.drawable.ic_nature_people_black_24dp);
break;
case "Java":
((MainActivity) getActivity()).setFragTitle("Java Quiz");
// topicLogo.setImageResource(R.drawable.ic_nature_people_black_24dp);
break;
case "C":
((MainActivity) getActivity()).setFragTitle("C Quiz");
((MainActivity) getActivity()).setFragLogo(R.drawable.ic_home_black_24dp);
break;
case "C++":
((MainActivity) getActivity()).setFragTitle("C++ Quiz");
break;
case "Python":
((MainActivity) getActivity()).setFragTitle("Python Quiz");
break;
case "Kotlin":
((MainActivity) getActivity()).setFragTitle("Kotlin Quiz");
break;
}
btnSubmit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (timerSwitch)
countDownTimer.cancel();
if (id == 0) {
non_attempted++;
arrAnswers.add("NotAttempted");
Toast.makeText(getActivity(), "Not Attempted!", Toast.LENGTH_SHORT).show();
}
switch (id) {
case R.id.boxA:
arrAnswers.add("A");
break;
case R.id.boxB:
arrAnswers.add("B");
break;
case R.id.boxC:
arrAnswers.add("C");
break;
case R.id.boxD:
arrAnswers.add("D");
break;
}
if ((id == R.id.boxA && ans.equals("A"))
|| (id == R.id.boxB && ans.equals("B"))
|| (id == R.id.boxC && ans.equals("C"))
|| (id == R.id.boxD && ans.equals("D"))) {
right++;
count++;
Toast.makeText(getActivity(), "RIGHT!", Toast.LENGTH_SHORT).show();
if (count < arrQues.size()) {
loadQues(timerSwitch);
} else {
sendResult();
}
} else {
count++;
if (count < arrQues.size()) {
loadQues(timerSwitch);
} else {
sendResult();
}
}
}
});
return view;
}
public void setBtnDefault() {
boxA.setBackgroundColor(getResources().getColor(android.R.color.transparent));
boxB.setBackgroundColor(getResources().getColor(android.R.color.transparent));
boxC.setBackgroundColor(getResources().getColor(android.R.color.transparent));
boxD.setBackgroundColor(getResources().getColor(android.R.color.transparent));
}
public void sendResult() {
int attempted = selectedVal - non_attempted;
Gson gson = new Gson();
String jsonAnswers = gson.toJson(arrAnswers);
String jsonQues = gson.toJson(arrQues);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putInt("right_key", right);
editor.putInt("wrong_key", attempted - right);
editor.putInt("total_key", selectedVal);
editor.putInt("attempted_key", attempted);
editor.putString("arr_answers", jsonAnswers);
editor.putString("arr_ques", jsonQues);
editor.commit();
((MainActivity) getActivity()).AddFrag(new ResultFragment(), 1);
}
public void LoadTimer() {
countDownTimer = new CountDownTimer(60000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
txtTimer.setText("0:" + millisUntilFinished / 1000);
}
#SuppressLint("SetTextI18n")
#Override
public void onFinish() {
txtTimer.setText("Time Over");
}
};
}
#SuppressLint("NewApi")
public void loadQues(boolean timer_switch) {
try {
id = 0;
setBtnDefault();
if (timer_switch) {
LoadTimer();
countDownTimer.start();
}
curr_quesNo++;
txtStatus.setText(curr_quesNo + "/" + selectedVal);
txtOptC.setVisibility(View.VISIBLE);
txtOptD.setVisibility(View.VISIBLE);
txtCode.setVisibility(View.VISIBLE);
main_params.removeRule(RelativeLayout.ABOVE);
submit_params.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
submit_params.addRule(RelativeLayout.BELOW, mainContainer.getId());
submit_params.topMargin = (int) convertPxToDp(getContext(), convertDpToPx(getContext(), 70));
mainContainer.setLayoutParams(main_params);
submitContainer.setLayoutParams(submit_params);
txtQues.setText(arrQues.get(count).ques);
txtOptA.setText(arrQues.get(count).optionA);
txtOptB.setText(arrQues.get(count).optionB);
txtOptC.setText(arrQues.get(count).optionC);
txtOptD.setText(arrQues.get(count).optionD);
txtCode.loadDataWithBaseURL(null, arrQues.get(count).code, "text/html", null, null);
if (txtOptC.getText().toString().isEmpty())
txtOptC.setVisibility(View.GONE);
if (txtOptD.getText().toString().isEmpty())
txtOptD.setVisibility(View.GONE);
if (arrQues.get(count).code.isEmpty())
txtCode.setVisibility(View.GONE);
if (!(arrQues.get(count).code.isEmpty())) {
submit_params.removeRule(RelativeLayout.BELOW);
submit_params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
submit_params.bottomMargin = (int) convertPxToDp(getContext(), convertDpToPx(getContext(), 15));
main_params.addRule(RelativeLayout.ABOVE, submitContainer.getId());
mainContainer.setLayoutParams(main_params);
submitContainer.setLayoutParams(submit_params);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
scrollView.arrowScroll(View.FOCUS_DOWN);
}
}, 1000);
}
ans = arrQues.get(count).answer;
boxA.setOnClickListener(this);
boxB.setOnClickListener(this);
boxC.setOnClickListener(this);
boxD.setOnClickListener(this);
} catch (Exception e) {
((MainActivity) getActivity()).AddFrag(new QuestionsFragment(), 1);
}
}
#Override
public void onClick(View v) {
setBtnDefault();
id = v.getId();
v.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
}
public float convertDpToPx(Context context, float dp) {
return dp * context.getResources().getDisplayMetrics().density;
}
public float convertPxToDp(Context context, float px) {
return px / context.getResources().getDisplayMetrics().density;
}
}
I solved it, all issues were happening because arrQues.get(count).code was fetching null values from the database(The column "Code" had null values). As soon as I replaced null values with empty strings "" isEmpty() worked perfectly. I guess isEmpty() doesn't work with null values and is only intended for empty strings.
I have a list of threads which I have paginated to use an endless scroll the issue I'm having (well my users) is an OutOfMemoryError: Failed to allocate a [x] byte allocation with [y] free bytes and [z] until OOM. the x, y and z attribute is different per user but the cause of the bug is always in the same place and it's when I refresh the posts. I'm completely out of my depth here as I have no idea how to optimise my code or make it so this doesn't happen. As it's the biggest crash on my app at the moment. I've posted my PostFragment below please see the refreshPosts(ArrayList<Posts> newObjects) method as this is where the crash is happening.
public class PostFragment extends Fragment implements View.OnClickListener {
private View mRootView;
private GridLayoutManager mLayoutManager;
private ThreadItem mThreads;
private PostItem mPost;
private PostAdapter mAdapter;
private PostResponse mData;
private EmoticonResponse mEmoticon;
private PostFeedDataFactory mDataFactory;
private EmoticonFeedDataFactory mEmoticonDataFactory;
private static PostFragment mCurrentFragment;
private int REQUEST_CODE;
//Flip
private boolean isFlipped = false;
private Animation flipAnimation;
#BindView(R.id.postsRecyclerView)
RecyclerView mRecyclerView;
#BindView(R.id.toolbarForPosts)
Toolbar mToolbar;
#BindView(R.id.threadText)
TextView mThreadText;
#BindView(R.id.flipText)
TextView mFlipTextView;
#BindView(R.id.shareText)
TextView mShareTextView;
#BindView(R.id.replyText)
TextView mReplyTextView;
#BindView(R.id.scrimColorView)
View mBackgroundView;
#BindView(R.id.fabMenu)
FloatingActionButton mFabMenu;
#BindView(R.id.flipFab)
FloatingActionButton mFlipFab;
#BindView(R.id.shareFab)
FloatingActionButton mShareFab;
#BindView(R.id.replyFab)
FloatingActionButton mReplyFab;
//Collapsing Toolbar
#BindView(R.id.postParentAppBarLayout)
AppBarLayout postAppBarLayout;
#BindView(R.id.postCollapseToolbar)
CollapsingToolbarLayout postCollapseToolbarLayout;
#BindView(R.id.mainImageContainer)
ViewGroup mainContainer;
//Back to top
#BindView(R.id.backToTopButton)
Button mBackToTop;
public static boolean isFromReply;
//FAB
private boolean mIsFabOpen = false;
private Animation fab_open, fab_close, rotate_forward, rotate_backward;
//Pagination
private int mCurrentPage = 1;
private ArrayList<Posts> postList = new ArrayList<>();
private boolean mIsLoading = false;
private boolean mIsLastPage = false;
public static PostFragment newInstance(#NonNull ThreadItem threadItem) {
Bundle args = new Bundle();
args.putParcelable("ThreadItem", Parcels.wrap(threadItem));
mCurrentFragment = new PostFragment();
mCurrentFragment.setArguments(args);
isFromReply = false;
return mCurrentFragment;
}
public static PostFragment newPostInstance(#NonNull PostItem postItem) {
Bundle args = new Bundle();
args.putParcelable("PostItemFromCompose", Parcels.wrap(postItem));
mCurrentFragment = new PostFragment();
mCurrentFragment.setArguments(args);
isFromReply = true;
return mCurrentFragment;
}
public PostFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.fragment_post, container, false);
if (savedInstanceState == null) {
ButterKnife.bind(this, mRootView);
initUI();
}
return mRootView;
}
private void initUI() {
//UI Setup
mLayoutManager = new GridLayoutManager(getActivity(), 1);
mRecyclerView.setLayoutManager(mLayoutManager);
mDataFactory = new PostFeedDataFactory(getActivity());
mEmoticonDataFactory = new EmoticonFeedDataFactory(getActivity());
TextView textThreadTopic = (TextView) mRootView.findViewById(R.id.threadTopic);
TextView textNumPosts = (TextView) mRootView.findViewById(R.id.numPosts);
//FAB onClick Set-Up
mFabMenu.setOnClickListener(this);
mShareFab.setOnClickListener(this);
mReplyFab.setOnClickListener(this);
mFlipFab.setOnClickListener(this);
//FAB Animation Set up
fab_open = AnimationUtils.loadAnimation(getActivity().getApplicationContext(),
R.anim.fab_open);
fab_close = AnimationUtils.loadAnimation(getActivity().getApplicationContext(),
R.anim.fab_close);
rotate_forward = AnimationUtils.loadAnimation(getActivity().getApplicationContext(),
R.anim.rotate_forward);
rotate_backward = AnimationUtils.loadAnimation(getActivity().getApplicationContext(),
R.anim.rotate_backward);
//Toolbar
((AppCompatActivity) getActivity()).setSupportActionBar(mToolbar);
((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayShowTitleEnabled(false);
mToolbar.setNavigationIcon(R.drawable.ic_back_white);
mToolbar.invalidate();
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getActivity().finish();
}
});
//Load Parcel
Intent intent = getActivity().getIntent();
mThreads = Parcels.unwrap(getArguments().getParcelable("ThreadItem"));
mPost = Parcels.unwrap(getArguments().getParcelable("PostItemFromCompose"));
if (mThreads != null) {
if (mThreads.getName() != null) {
mThreadText.setText(mThreads.getName());
}
if (mThreads.getTopic_name() != null) {
textThreadTopic.setText(mThreads.getTopic_name());
}
if (mThreads.getNum_posts() != null) {
int numPosts = Integer.parseInt(mThreads.getNum_posts());
if (numPosts > 1000) {
textNumPosts.setText("1K");
} else {
textNumPosts.setText(mThreads.getNum_posts());
}
}
}
postAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean isShow = false;
int scrollRange = -1;
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
if (scrollRange + verticalOffset == 0) {
postCollapseToolbarLayout.setTitle("Threads");
mainContainer.setVisibility(View.INVISIBLE);
isShow = true;
} else if (isShow) {
postCollapseToolbarLayout.setTitle("");
isShow = false;
mainContainer.setVisibility(View.VISIBLE);
}
}
});
flipAnimation =
AnimationUtils.loadAnimation(getActivity().getApplicationContext(), R.anim.flip);
loadData(true, 1);
}
private void loadData(final boolean firstLoad, int readDirection) {
if (isFromReply) {
if (mPost.getThread_id() != null) {
mDataFactory.getPostFeed(mPost.getThread_id(), readDirection, mCurrentPage,
new PostFeedDataFactory.PostFeedDataFactoryCallback() {
#Override
public void onPostDataReceived(PostResponse response) {
mData = response;
if (mData.getItems() != null) {
for (int i = 0; i < mData.getItems().size(); i++) {
Posts singlePost = response.getItems().get(i);
postList.add(singlePost);
}
if (firstLoad) {
mIsLoading = false;
mData.getItems().clear();
mData.getItems().addAll(postList);
mEmoticonDataFactory.getEmoticonFeed(
new EmoticonFeedDataFactory.EmoticonFeedDataFactoryCallback() {
#Override
public void onEmoticonDataReceived(EmoticonResponse response) {
mEmoticon = response;
populateUIWithData();
}
#Override
public void onEmoticonDataFailed(Exception exception) {
}
});
} else {
mIsLoading = false;
refreshPosts(postList);
}
if (mData.getItems().size() > 0) {
if (Integer.valueOf(mData.getTotalPosts()) >= response.getItems().size()) {
mCurrentPage++;
} else {
mIsLastPage = true;
}
}
}
}
#Override
public void onPostDataFailed(Exception exception) {
customToast("Error: " + exception.toString());
}
});
}
} else {
if (mThreads.getId() != null)
mDataFactory.getPostFeed(mThreads.getId(), readDirection, mCurrentPage,
new PostFeedDataFactory.PostFeedDataFactoryCallback() {
#Override
public void onPostDataReceived(PostResponse response) {
mData = response;
if (mData.getItems() != null) {
for (int i = 0; i < mData.getItems().size(); i++) {
Posts singlePost = response.getItems().get(i);
postList.add(singlePost);
}
if (firstLoad) {
mIsLoading = false;
mData.getItems().clear();
mData.getItems().addAll(postList);
mEmoticonDataFactory.getEmoticonFeed(
new EmoticonFeedDataFactory.EmoticonFeedDataFactoryCallback() {
#Override
public void onEmoticonDataReceived(EmoticonResponse response) {
mEmoticon = response;
populateUIWithData();
}
#Override
public void onEmoticonDataFailed(Exception exception) {
}
});
} else {
mIsLoading = false;
refreshPosts(postList);
}
if (mData.getItems().size() > 0) {
if (Integer.valueOf(mData.getTotalPosts()) >= response.getItems().size()) {
mCurrentPage++;
} else {
mIsLastPage = true;
}
}
}
}
#Override
public void onPostDataFailed(Exception exception) {
customToast("Error: " + exception.toString());
}
});
}
}
private void populateUIWithData() {
ImageButton moreOptionsButton = (ImageButton) mRootView.findViewById(R.id.moreOptions);
moreOptionsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
PopupMenu popupMenu = new PopupMenu(v.getContext(), v);
popupMenu.inflate(R.menu.thread_options);
popupMenu.getMenu();
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.watch:
WatchedThreadsRequestData watchedThreadsRequestData = new WatchedThreadsRequestData(getActivity());
watchedThreadsRequestData.setWatchedThread(mThreads.getId(), new WatchedThreadsRequestData.WatchedThreadsFeedback() {
#Override
public void onWatchedRequestReceived(ThreadResponse response) {
customToast("Thread watched");
}
#Override
public void onWatchedRequestFailed(Exception exception) {
customToast("Thread wasn't watched: " + exception.toString());
}
});
return true;
case R.id.shareThread:
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.putExtra(Intent.EXTRA_TEXT, mThreads.getName() + " - " + Constants.LIVE_URL +
"talk/" + mThreads.getTopic_url() + '/' + mThreads.getThread_url());
sharingIntent.setType("text/plain");
getActivity().startActivity(Intent.createChooser(sharingIntent, "Share via"));
return true;
case R.id.hideThread:
customToast("Hide: coming soon");
return true;
default:
customToast("Somethings Wrong");
return true;
}
}
});
setForceShowIcon(popupMenu);
popupMenu.show();
}
});
if (mAdapter == null) {
mAdapter = new PostAdapter(getActivity(), mData, mEmoticon);
mRecyclerView.setAdapter(mAdapter);
} else {
mAdapter.setData(mData.getItems());
mAdapter.notifyDataSetChanged();
}
mRecyclerView.addOnScrollListener(paginationListener);
}
public static void setForceShowIcon(PopupMenu popupMenu) {
try {
Field[] fields = popupMenu.getClass().getDeclaredFields();
for (Field field : fields) {
if ("mPopup".equals(field.getName())) {
field.setAccessible(true);
Object menuPopupHelper = field.get(popupMenu);
Class<?> classPopupHelper = Class.forName(menuPopupHelper
.getClass().getName());
Method setForceIcons = classPopupHelper.getMethod(
"setForceShowIcon", boolean.class);
setForceIcons.invoke(menuPopupHelper, true);
break;
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private RecyclerView.OnScrollListener paginationListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
boolean hasEnded = newState == SCROLL_STATE_IDLE;
if (hasEnded) {
mFabMenu.show();
mFabMenu.setClickable(true);
} else {
if (mIsFabOpen)
closeMenu();
mFabMenu.hide();
mFabMenu.setClickable(false);
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = mLayoutManager.getChildCount();
int totalItemCount = mLayoutManager.getItemCount();
int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();
if (!mIsLoading && !mIsLastPage) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount) {
loadMoreItems();
}
}
//Back to top
if (mLayoutManager.findLastVisibleItemPosition() == totalItemCount - 1) {
mBackToTop.setVisibility(View.VISIBLE);
mBackToTop.setClickable(true);
mBackToTop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mLayoutManager.scrollToPositionWithOffset(0,0);
}
});
} else {
mBackToTop.setVisibility(View.GONE);
mBackToTop.setClickable(false);
}
}
};
private void loadMoreItems() {
if (!isFlipped) {
mIsLoading = true;
loadData(false, 1);
} else {
mIsLoading = true;
loadData(false, -1);
}
}
private void refreshPosts(ArrayList<Posts> newObjects) {
postList.addAll(newObjects);
populateUIWithData();
}
#Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.fabMenu:
animateFAB();
break;
case R.id.shareFab:
share();
break;
case R.id.replyFab:
reply();
break;
case R.id.flipFab:
flip();
break;
}
}
public void animateFAB() {
if (mIsFabOpen) {
closeMenu();
} else {
mFabMenu.startAnimation(rotate_forward);
mReplyFab.startAnimation(fab_open);
mShareFab.startAnimation(fab_open);
mFlipFab.startAnimation(fab_open);
mReplyFab.setClickable(true);
mShareFab.setClickable(true);
mFlipFab.setClickable(true);
mFlipTextView.setVisibility(View.VISIBLE);
mShareTextView.setVisibility(View.VISIBLE);
mReplyTextView.setVisibility(View.VISIBLE);
mBackgroundView.setVisibility(View.VISIBLE);
mIsFabOpen = true;
}
}
private void closeMenu() {
mFabMenu.startAnimation(rotate_backward);
mReplyFab.startAnimation(fab_close);
mShareFab.startAnimation(fab_close);
mFlipFab.startAnimation(fab_close);
mReplyFab.setClickable(false);
mShareFab.setClickable(false);
mFlipFab.setClickable(false);
mFlipTextView.setVisibility(View.INVISIBLE);
mShareTextView.setVisibility(View.INVISIBLE);
mReplyTextView.setVisibility(View.INVISIBLE);
mBackgroundView.setVisibility(View.INVISIBLE);
mIsFabOpen = false;
}
private void reply() {
PreferenceConnector.writeString(getActivity().getApplicationContext(), "threadID", mThreads.getId());
PreferenceConnector.writeString(getActivity().getApplicationContext(), "threadTitle", mThreads.getName());
if (PreferenceConnector.readString(getActivity(), "authToken") == null ||
PreferenceConnector.readString(getActivity(), "authToken").equalsIgnoreCase("skip")) {
final AlertDialog.Builder loginDialog = new AlertDialog.Builder(getActivity());
loginDialog.setTitle("Please log in");
loginDialog.setMessage("You need to be logged in to reply");
loginDialog.setPositiveButton("Log in", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(getActivity().getApplicationContext(), LoginActivity.class);
startActivity(intent);
}
});
loginDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
loginDialog.show();
} else {
closeMenu();
Intent intent = new Intent(getActivity().getApplicationContext(), NewPostActivity.class);
intent.putExtra("Threads", Parcels.wrap(mThreads));
getActivity().finish();
startActivityForResult(intent, REQUEST_CODE);
}
}
private void share() {
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.putExtra(Intent.EXTRA_TEXT, mThreads.getName() + " - " + Constants.LIVE_URL +
"talk/" + mThreads.getTopic_url() + '/' + mThreads.getThread_url());
sharingIntent.setType("text/plain");
startActivity(Intent.createChooser(sharingIntent, "Share via"));
}
private void flip() {
if (!isFlipped) {
mAdapter.clearAll();
isFlipped = true;
mRecyclerView.startAnimation(flipAnimation);
loadData(false, -1);
closeMenu();
} else {
mAdapter.clearAll();
isFlipped = false;
mRecyclerView.startAnimation(flipAnimation);
loadData(true, 1);
closeMenu();
}
}
private void customToast(String toastMessage) {
LayoutInflater inflater = getActivity().getLayoutInflater();
View layout = inflater.inflate(R.layout.custom_toast,
(ViewGroup) getActivity().findViewById(R.id.toastContainer));
TextView customToastText = (TextView) layout.findViewById(R.id.customToastText);
customToastText.setText(toastMessage);
Toast toast = new Toast(getActivity().getApplicationContext());
toast.setGravity(Gravity.BOTTOM, 0, 25);
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
}
#Override
public void onResume() {
super.onResume();
if (mData != null && mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
if (mIsFabOpen) {
closeMenu();
} else {
getActivity().finish();
}
return true;
}
return false;
}
});
}
public void updateView() {
mAdapter.notifyDataSetChanged();
}
}
Thanks in advance once again.
Your problem basically boils down to this:
private void refreshPosts(ArrayList<Posts> newObjects) {
postList.addAll(newObjects);
populateUIWithData();
}
The list can only get bigger, never smaller. If the server has lots and lots of posts, then OutOfMemory is pretty much inevitable.
One approach to solving this problem is to use an LRU (Least Recently Used) cache. There is a utility class you can use: android.util.LruCache.
An LRU Cache is essentially a Map. Items are stored with a key, like an ID. With an LRU cache, you put new items in, but once a pre-determined limit is reached, old items start getting pushed out to make room for new items.
This will save memory, but make a lot more management code for you.
Your adapter, instead of having a list of posts, will just have a list of the post IDs. This should be much easier on memory.
As the user scrolls and you collect more posts, you add the post ID to the list, and map the post into the LRU cache using the post ID.
When you bind to the list item view, you look up the post using the post's ID in the LRU cache.
If it's there, great. That's called a cache hit. Bind the post to the
list item view.
If not, then you have a cache miss. You have some work to do.
Start a server request to retrieve the post by ID. I see your current code just retrieves blocks of posts, so you'll need some new server code here.
When the request completes, put the post in the LRU cache and let the adapter know your item has changed using adapter.notifyItemChanged(). Unless the user has scrolled beyond it, the RecyclerView should try to bind with the list item view again. This time, you should get a cache hit.
This is the basic idea. I'd write some code, but I still have a lot of questions since I can't see your model classes, data factories, and adapter class.
Once you have it working, you have to tune the limit on the cache so that it's low enough not to overrun memory, but high enough that your hit/miss ratio isn't close to zero.
BTW, I noticed that you are making the mistake of creating a new adapter and handing it to the RecyclerView each time you get a block of posts. You should create your adapter once, keep a reference to it and update it. Have a method that adds a block of posts then calls notifyDataSetChanged().
Another idea for conserving memory is to use text compression. If the problem is more a result of a large average size of the post rather than a large number of posts, you might explore this idea in addition to the LRU cache.
The concept is that you could take posts over a certain size, write them into a buffer using a ZipOutputStream then save the buffer in memory. When it's time to display the post, you read the buffer with a ZipInputStream to uncompress the text. Here the issue is performance as the compression/decompression is pretty CPU-intensive. But if the problem is really long posts, this approach might be something to consider.
An even better approach: Only save the first part of the post as an "overview" display in the list. When the user clicks on the list item, retrieve the entire post from the server and display that in another page.
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.