Android: line numbering - android

I'm actually trying to display the line numbering at the left of an EditText as shown in the figure.
The problem is that whenever a new line is added or removed to the file, it take a while to recalculate the string containing the line numbering.
What I do is the following: whenvere there is a type, I check whether the line count of the EditText has changed, if it is the case I just append the missing line at the end of the TextView showing the line numbering.
So if we take the piece of code in the image and press the new line key, my code get the line count (14 since we added a line) and append 14 + System.lineSeparator() to the TextView by using linesTextView.append(newText).
As said the application is lagging. Is there a way to efficently modify the content of a TextView?
Another solution that came in my mind was to use a TextView for each line number and just remove or add a new TextView when the line count of the content has changed. The problem about that is that I cannot make the TextViews be aligned with the lines of the EditText.
Thank you
EDIT:
Forgot to add the code
public class CodeEditView extends LinearLayout implements TouchEditText.TouchEditTextListener{
private View root;
private InternalFile file;
private TouchEditText code;
private ScrollView scroll;
private int linesCount;
private TextView lines;
private int currentStartLine;
private int currentEndLine;
public CodeEditView(Context context) {
super(context);
init(null, context);
}
public CodeEditView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, context);
}
private void init(AttributeSet attrs, Context context) {
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
root = layoutInflater.inflate(R.layout.edit_code_layout, this).findViewById(R.id.scroll_view);
this.lines = (TextView) findViewById(R.id.edit_code_lines_view);
this.code = (TouchEditText) findViewById(R.id.edit_code_content_view);
code.setRoot(root);
code.setListener(this);
linesCount = 0;
scroll = (ScrollView) findViewById(R.id.scroll_view);
scroll.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
// int scrollY = scroll.getScrollY();
// if(scrollY != 0) {
// code.scrollEvent();
// if(currentStartLine < code.getCurrentStartLine()) {
//
// String c = lines.getText().toString();
// String cc = c.replaceFirst("\\d+", " ");
// cc += Integer.toString(code.getCurrentEndLine()+10) + System.lineSeparator();
// lines.setText(cc);
// currentStartLine = code.getCurrentStartLine();
//
// } else {
//// CharSequence c = lines.getText();
//// lines.setText(lines.getText().);
// }
// }
int scrollY = scroll.getScrollY();
if(scrollY != 0) {
code.scrollEvent();
}
}
});
}
public void setFile(InternalFile file) {
this.file = file;
}
public void setLexer(Lexer lexer) {
code.setLexer(lexer);
}
private void initLines() {
// currentStartLine = code.getCurrentStartLine();
// currentEndLine = code.getCurrentEndLine();
// int usedLines = code.getLineCount();
// if(usedLines != linesCount) {
//
// String text = "";
//
// for(int i = currentStartLine; i <= currentEndLine+10; i++) {
// text += Integer.toString(i) + System.lineSeparator();
// }
// lines.setText(text);
// linesCount = usedLines;
// }
int usedLines = code.getLineCount();
String text = "";
for(int i = 1; i <= usedLines; i++) {
text += Integer.toString(i) + System.lineSeparator();
}
Log.d("TEXT", Integer.toString(usedLines));
lines.setText(text);
linesCount = usedLines;
}
public void initText(String content) {
code.initText(content);
code.post(new Runnable() {
#Override
public void run() {
initLines();
}
});
}
public void setFont(Typeface typeFace) {
this.lines.setTypeface(typeFace);
this.code.setTypeface(typeFace);
}
#Override
public void onTyped(String text) {
EventBus.getDefault().post(new UpdateCacheFileEvent(text,file));
setLines();
}
public void forceSyntax(Syntax s) {
code.forceSyntax(s);
}
private void setLines() {
int usedLines = code.getLineCount();
if(usedLines > linesCount) {
lines.append(Integer.toString(usedLines) + System.lineSeparator());
linesCount = usedLines;
} else {
}
}
}

Related

How to call a list of numbers on button click

I am creating an app which reads a list of numbers from a text file and shown in a list view, I can achieve that but dont have idea on how to schedule calls one after one
Example.xls
Consider a list of numbers in a file row wise
00000000000
62728828282
26727737338
My app should call one after one using intent after a button is clicked it should automatically call all the numbers
public class ScheduledFragment extends Fragment {
String[] strings;
RecyclerView listView;
Button button;
List<PhoneNumber> phoneNumbers;
public ScheduledFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_scheduled, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
listView = view.findViewById(R.id.list);
button = view.findViewById(R.id.btn);
FloatingActionButton fab = view.findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
requestPermission();
} else {
readExcelFileFromAssets();
initialiseAdapter();
}
}
});
final PhoneState phonestate = new PhoneState();
TelephonyManager telephonyManager = (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(phonestate,PhoneStateListener.LISTEN_CALL_STATE);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
InputStream myInput;
AssetManager assetManager = getActivity().getAssets();
myInput = assetManager.open("phone.xls");
Workbook workbook = Workbook.getWorkbook(myInput);
final Sheet sheet = workbook.getSheet(0);
final int row = sheet.getRows();
final int col = sheet.getColumns();
for (int i = 0; i < col; i++) {
for (int j = 0; j < row; j++) {
String number = "";
Cell cell;
cell = sheet.getCell(i,j);
number =number + cell.getContents();
Toast.makeText(getContext(),number+"",Toast.LENGTH_LONG).show();
if (!phonestate.isOnCall) {
callPhone(number);
}
}
}
} catch (Exception e) {
}
}
});
}
//Methods
private void requestPermission() {
ActivityCompat.requestPermissions(getActivity(), new String[]
{Manifest.permission.CALL_PHONE}, 1);
}
private void initialiseAdapter() {
listView.setHasFixedSize(true);
listView.setLayoutManager(new LinearLayoutManager(getContext()));
RecyclerAdapter recyclerAdapter = new RecyclerAdapter(phoneNumbers);
listView.setAdapter(recyclerAdapter);
}
public void callPhone(String number) {
Intent call = new Intent(Intent.ACTION_CALL);
call.setData(Uri.parse("tel:" + number));
startActivity(call);
}
public void readExcelFileFromAssets() {
phoneNumbers = new ArrayList<>();
try {
InputStream myInput;
AssetManager assetManager = getActivity().getAssets();
myInput = assetManager.open("phone.xls");
Workbook workbook = Workbook.getWorkbook(myInput);
Sheet sheet = workbook.getSheet(0);
int row = sheet.getRows();
int col = sheet.getColumns();
for (int i = 0; i < col; i++) {
for (int j = 0; j < row; j++) {
String number = "";
Cell cell;
cell = sheet.getCell(i, j);
number = number + cell.getContents();
phoneNumbers.add(new PhoneNumber(number));
}
}
} catch (Exception e) {
}
}
}
class PhoneState extends PhoneStateListener {
boolean isOnCall = true;
#Override
public void onCallStateChanged(int state, String phoneNumber) {
super.onCallStateChanged(state, phoneNumber);
if (TelephonyManager.CALL_STATE_IDLE == state) {
isOnCall = false;
} else if (TelephonyManager.CALL_STATE_RINGING == state) {
isOnCall = true;
} else if (TelephonyManager.CALL_STATE_OFFHOOK == state){
isOnCall = true;
}
}
}
class PhoneNumber {
String number;
public PhoneNumber() {
}
public PhoneNumber(String number) {
this.number = number;
}
}
Result I want
Should Call all the number one by one automatically after ending the call
Result I am getting
Calling only a single number which is randomly selected from file
Can anyone please tell me how to do this? I am new to this any help will be appreciated thanks
Refer this link here
Put this code after you have looped through all the numbers in .xls file.
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for(int i = 0;i <phoneNumbers.size();i++)
{
if (!phonestate.isOnCall)
{
callPhone(phoneNumbers.get(i).toString());
}
}
}

How to solve recyclerv view list update delay

There is a delay in updating the list of Recycler views. When items in the list are small, they are processed quickly, but when items are large, delays occur. The model of all items is the same, but it does not seem to be possible to use methods like NotifyItemChanged because it expresses different information and number.
#Override
public void onFolderCoverImgClick(int position) {
setVideoTopBarByFolderData(position);
AppVideoData.FolderData folderData = videoLayoutFolderList.get(position);
String folderId = folderData.getFolderId();
videoByFolderList.clear();
for (int i = 0; i < videoLayoutList.size(); i++) {
if (folderId.equals(videoLayoutList.get(i).getFolderId())) {
videoByFolderList.add(videoLayoutList.get(i));
}
}
String path = getFilesDir() + File.separator + "vid";
for (int i = 0; i < videoByFolderList.size(); i++) {
if (new File(path + File.separator + videoByFolderList.get(i).getId() + ".mp4").exists()) {
videoByFolderList.get(i).setVideoType("Download");
} else {
videoByFolderList.get(i).setVideoType("vimeo");
}
if(videoIdF.equals(videoByFolderList.get(i).getId())) {
videoPosF = i;
}
}
videoPortraitAdapter.updateList(videoByFolderList);
videoPortraitAdapter.checkSelectedVideoWhenFolder(videoPosF, videoIdF);
}
If I click on a specific view, it will be called back to the above method, and clear the list that was originally in the recycler view and update the new list to be shown. The rest of the code is responsible for additional processing. I tried to calculate the time using the 'System.currentTimeMillis' method, but it does not seem to cause delays in additional code.
public class VideoItemAdapter extends RecyclerView.Adapter<VideoItemAdapter.VideoViewHolder> {
public VideoItemAdapterListener videoItemAdapterListener;
public VideoItemDrawDoneListener videoItemDrawDoneListener;
private Context context;
private LiveActivity liveActivity;
private List<AppVideoData.AppYouTube> dataList;
private String skinColor;
private boolean fullScreen;
private boolean isFolder;
private int videoPosF;
private String videoIdF;
public VideoItemAdapter(Context context, List<AppVideoData.AppYouTube> dataList, String skinColor, boolean fullScreen, boolean isFolder) {
this.context = context;
this.dataList = dataList;
this.skinColor = skinColor;
this.fullScreen = fullScreen;
if (context instanceof LiveActivity) {
liveActivity = (LiveActivity) context;
}
this.isFolder = isFolder;
}
public void updateList(List<AppVideoData.AppYouTube> dataList) {
this.dataList = dataList;
notifyDataSetChanged();
}
public void setOnVideoItemClickListener(VideoItemAdapterListener videoItemAdapterListener) {
this.videoItemAdapterListener = videoItemAdapterListener;
}
public void setOnVideoItemDrawDoneListener(VideoItemDrawDoneListener videoItemDrawDoneListener) {
this.videoItemDrawDoneListener = videoItemDrawDoneListener;
}
#NonNull
#Override
public VideoItemAdapter.VideoViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.video_item, parent, false);
VideoViewHolder holder = new VideoViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull final VideoItemAdapter.VideoViewHolder holder, final int position) {
Log.d("time", "onBindViewHolder: " + System.currentTimeMillis());
int adapterPosition = 0;
if(holder.getAdapterPosition() != RecyclerView.NO_POSITION) { adapterPosition = holder.getAdapterPosition(); }
AppVideoData.AppYouTube videoData = dataList.get(adapterPosition);
String videoType = videoData.getVideoType();
Glide.with(context).load(videoData.getVideoThumbnail()).into(holder.itemThumbnail);
holder.itemSelected.setImageResource(R.drawable.shining_selected);
holder.itemTitle.setText(videoData.getTitle());
holder.itemDivider.setBackgroundColor(Color.parseColor(skinColor));
holder.itemDuration.setText(getDuration(videoData.getDuration()));
changeVideo(holder, adapterPosition, videoData.getId());
if (!videoType.equals("Download")) {
holder.itemDownload.setVisibility(View.VISIBLE);
holder.itemDelete.setVisibility(View.INVISIBLE);
} else {
holder.itemDownload.setVisibility(View.INVISIBLE);
holder.itemDelete.setVisibility(View.VISIBLE);
}
if(fullScreen) {
holder.itemDownload.setVisibility(View.INVISIBLE);
holder.itemDelete.setVisibility(View.INVISIBLE);
}
Log.d("time", "onBindViewHolder1: " + System.currentTimeMillis());
}
#Override
public int getItemCount() {
return (dataList != null) ? dataList.size() : 0;
}
private String getDuration(int original) {
int hour = original / 60 / 60;
int min = (original - (hour * 60 * 60)) / 60;
int sec = original - (hour * 60 * 60) - (min * 60);
return (hour > 0) ? String.format("%02d:%02d:%02d", hour, min, sec) : String.format("%02d:%02d", min, sec);
}
public void checkSelectedVideoWhenFolder(int videoPosF, String videoIdF) {
this.videoPosF = videoPosF;
this.videoIdF = videoIdF;
notifyItemChanged(videoPosF);
}
private void changeVideo(VideoViewHolder holder, int position, String videoId) {
if (!liveActivity.isFolder) {
if ((liveActivity.videoPos == position)) {
holder.itemSelected.setVisibility(View.VISIBLE);
} else {
holder.itemSelected.setVisibility(View.INVISIBLE);
}
} else {
if ((videoPosF == position) && videoIdF.equals(videoId)) {
holder.itemSelected.setVisibility(View.VISIBLE);
} else {
holder.itemSelected.setVisibility(View.INVISIBLE);
}
}
}
public class VideoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
protected RelativeLayout itemBody;
protected TextView itemTitle;
protected TextView itemDuration;
protected TextView itemDivider;
protected ImageView itemThumbnail;
protected ImageView itemSelected;
protected ImageButton itemDownload;
protected ImageButton itemDelete;
public VideoViewHolder(View itemView) {
super(itemView);
itemBody = (RelativeLayout) itemView.findViewById(R.id.video_item_body);
itemTitle = (TextView) itemView.findViewById(R.id.video_item_title);
itemDuration = (TextView) itemView.findViewById(R.id.video_item_duration);
itemDivider = (TextView) itemView.findViewById(R.id.video_item_divider);
itemThumbnail = (ImageView) itemView.findViewById(R.id.video_item_thumbnail);
itemSelected = (ImageView) itemView.findViewById(R.id.video_item_selected);
itemDownload = (ImageButton) itemView.findViewById(R.id.video_item_download);
itemDelete = (ImageButton) itemView.findViewById(R.id.video_item_delete);
itemBody.setOnClickListener(this);
itemDownload.setOnClickListener(this);
itemDelete.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.video_item_body:
videoItemAdapterListener.onVideoItemClick(getAdapterPosition(), fullScreen, itemSelected);
break;
case R.id.video_item_download:
videoItemAdapterListener.onVideoDownloadClick(getAdapterPosition(), fullScreen);
break;
case R.id.video_item_delete:
videoItemAdapterListener.onVideoDeleteClick(getAdapterPosition(), fullScreen);
break;
}
}
}
This is a recycler view adapter class. In the onBindViewHolder method of the adapter, even if I calculate the time using System.currentTimeMillis, I can not see anything that delay could happen. It took 0.005 to 0.01 seconds. There is a place of doubt, but I do not know exactly where to check it. It looks like there is a difference of about 0.6 seconds between 'onFolderCoverImgClick' and onBindViewHolder.
videoPortraitAdapter is an instance of VideoItemAdapter. If you know anyone, I would appreciate it if you could.
You can use new ListAdapter instead of RecyclerView.Adapter which provides the submitList() method which manages the differences in the lists and updates those only. Refer this link.
Better alternative option is by using setting up swipe to refresh and use
mSwipeRefreshLayout.setRefreshing(false); - in place where updating the adapter is done or NotifyItemChanged.
This has always worked for me, hope this helps you.

Android two-way data binding doesn't work with edittext and observable variable

I have a RecyclerView whose cells are populated through data-binding. Each cell represents an item for a cart of products. Each cell contains an EditText that is responsable for the product quantity in the cart. The quantity is reprezented as an observableInt in my ViewModel. When the quantity changes I want to make an action and I have an OnPropertyChangedCallback listener set for the observableInt parameter. If I use app:addTextChangedListener="#{cartItemVM.quantityInputTextWatcher}" to get the value and set it to the observable, its listener will be called for several times(that is also because I can change the value of the EditText from some + - buttons as well).
I run into Two-way data binding, but I still cannot make it work. This is what I have so far:
<QuantityEditText
android:id="#+id/etQuantityInput"
android:text="#{String.valueOf(cartItemVM.totalInCart)}"
quantity="#={cartItemVM.totalInCart}"
onQuantityChange="#{cartItemVM.onQuantityChange}"
This is my custom EditText:
public class QuantityEditText extends CustomEditText {
private int quantity;
private OnQuantityChangeListener onQuantityChangeListener;
public interface OnQuantityChangeListener {
void onQuantityChange(QuantityEditText view, int quantity);
}
This is my ViewModel class:
#InverseBindingMethods({
#InverseBindingMethod(type = QuantityEditText.class, attribute = "quantity")
})
public class ProductInCartObservableViewModel{
public final ObservableInt totalInCart;
#BindingAdapter(value = {"onQuantityChange", "quantityAttrChanged"},
requireAll = false)
public static void setQuantityAttrChanged(QuantityEditText view,
final QuantityEditText.OnQuantityChangeListener listener,
final InverseBindingListener quantityChange) {
if (quantityChange == null) {
view.setOnQuantityChangeListener(listener);
} else {
view.setOnQuantityChangeListener(new QuantityEditText.OnQuantityChangeListener() {
#Override
public void onQuantityChange(QuantityEditText view, int quantity) {
if (listener != null) {
listener.onQuantityChange(view, quantity);
}
view.setText(String.valueOf(quantity));
quantityChange.onChange();
}
});
}
}
#BindingAdapter("quantity")
public static void setQuantity(QuantityEditText view, int quantity) {
if (quantity != view.getQuantity()) {
view.setQuantity(quantity);
}
}
#InverseBindingAdapter(attribute = "quantity")
public static int getQuantity(QuantityEditText view) {
int val = 0;
if (!TextUtils.isEmpty(view.getText().toString())) {
try {
val = Integer.valueOf(view.getText().toString());
} catch (IllegalArgumentException e) {
Timber.e(e);
}
}
// Won't let the user remove product from cart using the editText
if (val <= 0) {
val = 1;
}
if (val > 150) {
val = 150;
}
return val;
}
public QuantityEditText.OnQuantityChangeListener onQuantityChange = new QuantityEditText.OnQuantityChangeListener() {
#Override
public void onQuantityChange(QuantityEditText view, int quantity) {
if (quantity <= 0) {
quantity = 0;
}
if (quantity > 150) {
quantity = 150;
}
totalInCart.set(quantity);
}
};
The implementation is taken from different places, but I admit I haven't fully understood the process, so an explanation will be highly appreciated too.
I can think of a couple of ways to solve the problem. The first is to avoid the QuatityEditText and just set the number directly. I prefer using conversion methods to custom two-way binding because they tend to be easier to implement:
#InverseMethod("stringToInt")
public static String intToString(TextView view, int oldValue, int value) {
NumberFormat numberFormat = getNumberFormat(view);
try {
String inView = view.getText().toString();
int parsed = numberFormat.parse(inView).intValue();
if (parsed == value) {
return view.getText().toString();
}
} catch (ParseException e) {
// old number was broken
}
return numberFormat.format(value);
}
public static int stringToInt(TextView view, int oldValue, String value) {
NumberFormat numberFormat = getNumberFormat(view);
try {
return numberFormat.parse(value).intValue();
} catch (ParseException e) {
view.setError("Improper number format");
return oldValue;
}
}
private static NumberFormat getNumberFormat(View view) {
Resources resources= view.getResources();
Locale locale = resources.getConfiguration().locale;
NumberFormat format =
NumberFormat.getNumberInstance(locale);
if (format instanceof DecimalFormat) {
DecimalFormat decimalFormat = (DecimalFormat) format;
decimalFormat.setGroupingUsed(false);
}
return format;
}
and the layout has:
<EditText ...
android:id="#+id/etQuantityInput"
android:inputType="number"
android:text="#={Converter.intToString(etQuantityInput, cartItemVM.totalInCart, cartItemVM.totalInCart)}"/>
If you want a new QuantityEditText, I think it is best to have one attribute control it rather than both the string text and int quantity:
<QuantityEditText ...
android:id="#+id/etQuantityInput"
android:inputType="number"
app:quantity="#={cartItemVM.totalInCart)}"/>
Here is the class that I put together so that quantity follows the text:
public class QuantityEditText extends android.support.v7.widget.AppCompatEditText {
public QuantityEditText(Context context) {
super(context);
initTextWatcher();
}
public QuantityEditText(Context context, AttributeSet attrs) {
super(context, attrs);
initTextWatcher();
}
public QuantityEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initTextWatcher();
}
private int quantity;
private OnQuantityChangeListener onQuantityChangeListener;
void initTextWatcher() {
addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
updateQuantityFromText();
}
});
}
public void setQuantity(int quantity) {
if (updateQuantity(quantity)) {
setText(getNumberFormat().format(quantity));
}
}
public int getQuantity() {
return quantity;
}
private boolean updateQuantity(int newQuantity) {
if (this.quantity == newQuantity) {
return false; // nothing to do
}
this.quantity = newQuantity;
if (onQuantityChangeListener != null) {
onQuantityChangeListener.onQuantityChange(this, quantity);
}
return true;
}
void updateQuantityFromText() {
try {
String inView = getText().toString();
updateQuantity(getNumberFormat().parse(inView).intValue());
} catch (ParseException e) {
// Problem with the string format, so just don't update the quantity
}
}
private NumberFormat getNumberFormat() {
Resources resources = getResources();
Locale locale = resources.getConfiguration().locale;
NumberFormat format =
NumberFormat.getNumberInstance(locale);
if (format instanceof DecimalFormat) {
DecimalFormat decimalFormat = (DecimalFormat) format;
decimalFormat.setGroupingUsed(false);
}
return format;
}
public void setOnQuantityChangeListener(OnQuantityChangeListener listener) {
onQuantityChangeListener = listener;
}
public interface OnQuantityChangeListener {
void onQuantityChange(QuantityEditText view, int quantity);
}
}
You also need a way to set the InverseBindingListener and setup two-way data binding for quantity:
#InverseBindingMethods(
#InverseBindingMethod(type = QuantityEditText.class, attribute = "quantity")
)
public class BindingAdapters {
#BindingAdapter("quantityAttrChanged")
public static void setQuantityAttrChanged(QuantityEditText view,
final InverseBindingListener quantityChange) {
QuantityEditText.OnQuantityChangeListener listener = null;
if (quantityChange != null) {
listener = new QuantityEditText.OnQuantityChangeListener() {
#Override
public void onQuantityChange(QuantityEditText view, int quantity) {
quantityChange.onChange();
}
};
}
view.setOnQuantityChangeListener(listener);
}
}

Android ShowcaseView not showing with recursion method

Straight to the point. I am trying to show coachmark series using ShowcaseView library by amlcurran: https://github.com/amlcurran/ShowcaseView
I can't get the coachmark showing. The problem is runCoachMark() method always returning null. runCoachMark() method run right after notifyDatasetChaned() on mMainAdapter.
I have tried to use Handler().postDelayed() and Thread.sleep() with no success.
Anyone can explain why it is happening and some solutions for this problem. Thank you.
private static final int COACHMARK_A = -3;
private static final int COACHMARK_B = -2;
private static final int COACHMARK_C = -1;
// ... some other coachmark type
private static final int END_COACHMARK = 0;
private static final int SCROLL_OFFSET = 56;
private static final long COACHMARK_DELAY = 200L;
private void runCoachMark(int type) {
if (type == END_COACHMARK) {
return;
}
View v = getCoachMarkView(type);
if (v == null) {
return;
}
showCoachMark(getActivity(), v, type);
}
#Nullable
private View getCoachMarkView(final int type) {
safeScrollTo(getPos(type), SCROLL_OFFSET);
return mMainList.getChildAt(0).findViewById(getCoachMarkId(type));
}
private void safeScrollTo(final int pos, final int offset) {
mMainList.setLayoutFrozen(true);
mLayoutManager.scrollToPositionWithOffset(pos, offset);
mMainList.setLayoutFrozen(false);
}
private int getCoachMarkId(int type) {
int id;
switch (type) {
case COACHMARK_A:
id = R.id.A;
break;
case COACHMARK_B:
id = R.id.B;
break;
case COACHMARK_C:
id = R.id.C;
break;
// ... some other types
default:
id = 0;
break;
return id;
}
private int getPos(int type) {
int pos;
switch (type) {
case COACHMARK_A:
pos = 1;
break;
case COACHMARK_B:
pos = 4;
break;
case COACHMARK_C:
pos = 5;
break;
// ... some other cases
default:
pos = 0;
break;
}
return pos;
}
private void showCoachMark(final Context context, final View v, final int type) {
new ShowcaseView.Builder(getActivity())
.setTarget(new ViewTarget(v.getId(), (Activity) context))
.setContentTitle(getTitle(type))
.setContentText(getDescription(type))
.setShowcaseDrawer(new CustomShowcaseView(v))
.setShowcaseEventListener(new OnShowcaseEventListener() {
#Override
public void onShowcaseViewHide(ShowcaseView showcaseView) {
}
#Override
public void onShowcaseViewDidHide(ShowcaseView showcaseView) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
runCoachMark(type++);
}
}, COACHMARK_DELAY);
}
#Override
public void onShowcaseViewShow(ShowcaseView showcaseView) {
}
#Override
public void onShowcaseViewTouchBlocked(MotionEvent motionEvent) {
}
})
.build();
}
}
I would share with you a new way to use showcaseview dynamically with recursive method
1) Create your ViewTargets, for example 4 targets (you can create n targets) and put targets into an array declared as a private attribute in your Activity class : private ViewTarget[] targets;
final ViewTarget target1 = new ViewTarget(textview1);
final ViewTarget target2 = new ViewTarget(textview2);
final ViewTarget target3 = new ViewTarget(textview3);
final ViewTarget target4 = new ViewTarget(textview4);
targets = new ViewTarget[] { target1, target2,target3,target4};
2) Create the recursive method :
private void toStep(final String title, final String description,Target target, final int i) {
final ShowcaseView.Builder showCaseBuilder = new ShowcaseView.Builder(ShowCaseActivity.this);
showCaseBuilder.setTarget(targets[i]);
showCaseBuilder.setContentTitle(title.replace("[i]", ""+i));
showCaseBuilder.setContentText(description.replace("[i]", ""+i));
showCaseBuilder.setShowcaseEventListener(new OnShowcaseEventListener() {
#Override
public void onShowcaseViewTouchBlocked(MotionEvent motionEvent) {
}
#Override
public void onShowcaseViewShow(ShowcaseView showcaseView) {
}
#Override
public void onShowcaseViewHide(ShowcaseView showcaseView) {
try {
toStep(title, description, targets[i+1], i+1);
} catch(Exception ex) {
}
}
#Override
public void onShowcaseViewDidHide(ShowcaseView showcaseView) {
}
});
showCaseBuilder.build();
}
3) Finally you can call the method that displays Showcases :
toStep("Title for showcase [i]", " Description for showcase [i] ",targets[0], 0);
I hope this will be helpful for you :)

Load App Images at Runtime

I want my Android application to work with different skins. So what I plan is changing all the images and fonts I used in the application for different skins.
What is the best way for this? How can i init all imageviews with proper image resources?
P.S : I want to change the skin at runtime and don't want to restart the app.
Thanks..
you can create a ThemeManager class where you can manage the themes
public class ThemeManager {
static ThemeManager tmanage;
int ThemeCode;
int topBarBackground, bottomBarBackground, buttonBackground, textColor;
ArrayList<ThemeWrapper> twrapper = new ArrayList<ThemeWrapper>();
public static ThemeManager getInstance() {
if (tmanage != null)
return tmanage;
else
tmanage = new ThemeManager();
return tmanage;
}
public void setThemeCode(int themeCode) {
ThemeCode = themeCode;
setBottomBarBackground(themeCode);
setTopBarBackground(themeCode);
setButtonBackground(themeCode);
setTextColor(themeCode);
}
#SuppressWarnings("deprecation")
public void setTheme(ArrayList<View> v) {
for (int i = 0; i < v.size(); i++) {
if (v.get(i) instanceof RelativeLayout) {
RelativeLayout layout = (RelativeLayout) v.get(i);
layout.setBackgroundColor(getTopBarBackground());
}
else if (v.get(i) instanceof Button) {
Button layout = (Button) v.get(i);
layout.setBackgroundDrawable(getShape(getButtonBackground()));
layout.setPadding(5, 5, 5, 5);
layout.setTextSize(12);
}
else if (v.get(i) instanceof LinearLayout) {
LinearLayout layout = (LinearLayout) v.get(i);
layout.setBackgroundColor(getBottomBarBackground());
}
else if (v.get(i) instanceof TextView) {
TextView layout = (TextView) v.get(i);
layout.setTextColor(getTextColor());
}
}
}
private Drawable getShape(int col){
GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TL_BR, new int[] { col,
col, col});
gradientDrawable.setGradientType(GradientDrawable.RECTANGLE);
gradientDrawable.setCornerRadii(getRandomFloatArray());
gradientDrawable.setGradientCenter(0.0f, 0.45f);
gradientDrawable.setStroke(1, Color.WHITE);
gradientDrawable.setSize(60, 30);
return gradientDrawable;
}
private float [] getRandomFloatArray(){
float[] floats = new float[8];
for (int i =0; i < floats.length; i++){
floats[i] = 2;
}
return floats;
}
public int getTopBarBackground() {
return topBarBackground;
}
public void setTopBarBackground(int topBarBackground) {
this.topBarBackground = topBarBackground;
}
public int getBottomBarBackground() {
return bottomBarBackground;
}
public void setBottomBarBackground(int bottomBarBackground) {
this.bottomBarBackground = bottomBarBackground;
}
public int getButtonBackground() {
return buttonBackground;
}
public void setButtonBackground(int buttonBackground) {
this.buttonBackground = buttonBackground;
}
public int getTextColor() {
return textColor;
}
public void setTextColor(int textColor) {
this.textColor = textColor;
}
}
I was changing the colors for the views you can set the drawables as well now use this in your activities onResume function
private void changeColor() {
// TODO Auto-generated method stub
ArrayList<View> v = new ArrayList<View>();
v.add(topBar);
v.add(bottomBar);
v.add(updateBio);
v.add(updateEvents);
v.add(updateGallery);
v.add(updatePersonalStyle);
v.add(updateVip);
v.add(updateStatus);
v.add(NameLink);
v.add(changeProfilePicture);
ThemeManager theme = ThemeManager.getInstance();
theme.setThemeCode(PreferenceManager.getDefaultSharedPreferences(
getApplicationContext()).getInt("Theme_Color",
Color.parseColor("#7606a5")));
theme.setTheme(v);
}
just call this function in the onResume

Categories

Resources