Categories
discuss

PMD UselessParentheses violation

I have the following Java method:

private int calculate() {
    return (bytes[0] & 0xff) + ((bytes[1] & 0xff) << 8);
}

PMD complains on this code with “UselessParentheses” violation.

I’ve reviewed operator precentence rules and I still don’t see redundant parentheses in that code. Am I missing something?

Answer

There’s no unnecessary parenthesis in this code, as you can see if you run this:

        byte [] bytes = new byte[] {1,2};

        System.out.println( (bytes[0] & 0xff) + ((bytes[1] & 0xff) << 8));
        System.out.println( bytes[0] & 0xff + ((bytes[1] & 0xff) << 8));
        System.out.println( (bytes[0] & 0xff) + (bytes[1] & 0xff) << 8);
        System.out.println( (bytes[0] & 0xff) + (bytes[1] & 0xff << 8));

Moreover, sometimes it’s actually good to add extra parentheses for readability. For example:

int i = x << y + z;   // this will shift x by y+z bits
int j = x << (y + z); // equivalent, but more readable
Categories
discuss

How to format a string for different language with different arguments in Android?

I have two string resources. The first one in mainvalues is in English and doesn’t have a string argument:

<string name="example">Have a good day!</string>

But in French, under values-fr, we have a different phrase with one argument, like so:

<string name="example">Bonne journée M. %1$s!</string>

How should I use the resource string formatter? This works, but it doesn’t seem right (and it generates a lint warning):

textView.setText(getString(R.string.example, name));

The lint warning is “StringFormatInvalid” and the description is “Format string ‘example‘ is not a valid format string so it should not be passed to String.format

Answer

There is nothing wrong with your approach. The formatter works perfectly even if there are too many arguments. The leftover arguments just get dropped.

Lint warnings are there to indicate possible unintended errors in the code. But if you know what you are doing you just may suppress the warning by adding the @SuppressLint("StringFormatMatches") annotation to the enclosing method or class.

Categories
discuss

Android Programming crop background image

I want to display an image as background in my app. Now I used this statement: android:background="@drawable/background" in <LineraLayout>. Here is the .xml Code:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="overbit.user.MainActivity"
    android:background="@drawable/background">
</LinearLayout>

Now I get this output:

image

But I want it like this:

as

Maybe someone of you can help me. Thanks.

Answer

There is BitmapDrawable, which allows to do it. First you need to create a drawable resource as follows (let it be a file in the project resources res/drawable/mybg.xml ):

<bitmap
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/background"
    android:gravity="center" />

And then specify it as a background resource in your layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="overbit.user.MainActivity"
    android:background="@drawable/mybg">
</LinearLayout>
Categories
discuss

how to set direction of whole application to RTL?

I’m using API 17 and I’m looking for a way to set the whole app to rtl instead of using <android:layoutDirection="rtl"> in every single activity layout.

Answer

I had the same problem , all you need to do is just consider all your used parent container (LinearLayout , RelativeLayout , GridView , etc) and set the LayoutDirection to RTL you can approach this way programmtically thanks to ViewCompat class to include api lower than 17.

ViewCompat.setLayoutDirection(yourParentContainer,ViewCompat.LAYOUT_DIRECTION_RTL);

note that there is no need to set the Direction for childviews separately

Categories
discuss

ViewHolder Layout Updates don’t apply from onBindViewHolder()

The setup I’ve got a RecyclerView with a custom adapter and custom ViewHolders.

I understand the onCreateViewHolder() method is called when the RecyclerView is running out of ViewHolders to recycle and needs a new one. So I’m just inflating a layout in there and passing it to a new ViewHolder.

Furthermore, onBindViewHolder() is responsible for filling the ViewHolder with data as soon as a new ViewHolder has been created or recycled by the RecyclerView. So what I’m doing in there is calling my method holder.setNode() to pass a data object to the ViewHolder.

The behavior I’m seeing When the activity first launches, all entries are correct. When I’m adding new entries or deleting existing ones, however, things start to get a bit funny.

  • the title TextView is always set correctly
  • the background color of the main layout changes seemingly at will, I’m assuming because the RecyclerView is reusing old ones
  • as does the custom view I have implemented, even though I’m invalidating it and passing it new values which change its appearance noticeably

So I’m wondering: Why aren’t those values changed in onBindViewHolder() as soon as views get reused? Or if I’m wrong, what’s the real reason for the random switching of layouts?


TaskListAdapter

class TaskListAdapter extends RecyclerView.Adapter<TaskListAdapter.TaskViewHolder> {

private ArrayList<NodeHandler.DbNode> dbNodeList = new ArrayList<>();
...

@Override
public TaskViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.small_task_view, parent, false);
    return new TaskViewHolder(v);
}

@Override
public void onBindViewHolder(TaskViewHolder holder, int position) {
    final NodeHandler.DbNode dbNode = dbNodeList.get(position);
    holder.setNode(dbNode);
    holder.wrapper.findViewById(R.id.card_details).setVisibility(View.GONE);
}

...

public static class TaskViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder {
    private FrameLayout wrapper;
    private TextView title;
    private NodeHandler.DbNode dbNode;

    public TaskViewHolder(View view) {
        ...
    }

    public void setTitle(String str) {
        title.setText(str);
    }

    public void setMarkers(@IntRange(from = 1, to = Node.MAX_URGENCY) int urgency, @IntRange(from = 1, to = Node.MAX_IMPORTANCE) int importance) {
        if(!dbNode.isAppointment()) {
            wrapper.setBackgroundColor(ContextCompat.getColor(wrapper.getContext(), R.color.lightGray));
        }
        ((QuadrantView) wrapper.findViewById(R.id.quadrant_view)).setDimensions(importance, urgency);
        // setDimensions will invalidate the view
    }

    public void setNode(NodeHandler.DbNode dbNodeObject) {
        this.dbNode = dbNodeObject;
        setTitle(dbNode.toString());
        setMarkers(dbNode.getUrgency(), dbNode.getImportance());
        setTips();
    }
}
}

Let me know if anything else could matter here. I’d be happy to update the question accordingly.

Answer

Values are indeed changed in onBindViewHolder as soon as views get reused.

The real reason for the seemingly random switching of layouts is that onBindViewHolder is currently implemented in a way that assumes that the ViewHolder was freshly created and is being bound for its first time. onBindViewHolder should instead be implemented in a way that assumes that the ViewHolder being bound is being reused so it should either:

  1. reset all the values of the ViewHolder to default values first before setting them to other values or
  2. make sure that everything is set inside onBindViewHolder, so one cannot tell that it was ever previously bound to something else.

Random background color changes:

You are right for suspecting that the random background color problem is caused by the RecyclerView reusing ViewHolders.

The reason why this is happening is because of the following code:

    if(!dbNode.isAppointment()) {
        wrapper.setBackgroundColor(ContextCompat.getColor(wrapper.getContext(), R.color.lightGray));
    }

It only sets the background if the ViewHolder is not an appointment. So if a ViewHolder that is being reused was previously not for an appointment, but is currently for one that is now an appointment, it’s background color will be inappropriate.

to fix this, do any of the following:

  • set the background color of the ViewHolder to some default color before the if statement is executed (as per solution 1 mentioned above):

    wrapper.setBackgroundColor(/* default background color */);
    if(!dbNode.isAppointment()) {
        wrapper.setBackgroundColor(ContextCompat.getColor(wrapper.getContext(), R.color.lightGray));
    }
    
  • add an else block to the if statement to set the background color of the ViewHolder to the appropriate color (as per solution 2 mentioned above)

    if(!dbNode.isAppointment()) {
        wrapper.setBackgroundColor(ContextCompat.getColor(wrapper.getContext(), R.color.lightGray));
    }
    else
    {
        wrapper.setBackgroundColor(/* appointment background color */);
    }
    
  • override the RecyclerView.Adapter‘s getItemViewType to return different view types based on dbNode.isAppointment(), and create different ViewHolder subclasses for displaying each of them

p.s. I don’t know what the problem could be regarding the custom views…sorry

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..