เปรียบเทียบสอง drawables ใน Android


90

วิธีเปรียบเทียบสอง drawables ฉันกำลังทำเช่นนี้ แต่ไม่ประสบความสำเร็จใด ๆ

public void MyClick(View view)
{
 Drawable fDraw = view.getBackground();
 Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);

  if(fDraw.equals(sDraw))
  {
   //Not coming
  }
}

คำตอบ:


150

อัปเดต https://stackoverflow.com/a/36373569/1835650

getConstantState () ทำงานได้ไม่ดี

มีอีกวิธีหนึ่งในการเปรียบเทียบ:

mRememberPwd.getDrawable().getConstantState().equals
            (getResources().getDrawable(R.drawable.login_checked).getConstantState());

mRemeberPwdเป็นImageViewตัวอย่างนี้ หากคุณใช้ a TextViewให้ใช้getBackground().getConstantStateแทน


3
โซลูชันนี้ใช้งานได้และดีกว่าเนื่องจากหลีกเลี่ยงการแปลง Drawable เป็น Bitmap และเปรียบเทียบ
Braj

2
ไม่เสมอไป: WallpaperManager.getInstance (this) .getFastDrawable (). getConstantState () เป็น null
paulgavrikov

ขออภัยที่ยอมรับสิ่งนี้เป็นคำตอบช้าและเปลี่ยนคำตอบของตัวเองเนื่องจากดูเหมือนว่าเป็นตัวเลือกที่ดีกว่า (และมีการโหวตเพิ่มขึ้นด้วย :)) ดังนั้นตรวจสอบสิ่งนี้เป็นคำตอบ
Roshan Jha

ขอบคุณนี่คือคำตอบที่ดีที่สุด
satyres

8
รหัสนี้ใช้งานได้ดีกับอุปกรณ์ที่ต่ำกว่า 5.0 แต่ฉันได้รับข้อผิดพลาดเมื่อใช้บนอุปกรณ์ 5.0 ทุกคนสามารถยืนยันว่าวิธีนี้ใช้ได้หรือไม่บน Android 5.0 ขึ้นไป
Phil3992

41

อาศัยgetConstantState()อยู่คนเดียวจะส่งผลในเชิงลบเท็จ

แนวทางที่ฉันใช้คือลองเปรียบเทียบ ConstantState ในอินสแตนซ์แรก แต่ถอยกลับไปเปรียบเทียบกับ Bitmap หากการตรวจสอบนั้นล้มเหลว

สิ่งนี้ควรใช้งานได้ในทุกกรณี (รวมถึงรูปภาพที่ไม่ใช่ทรัพยากร) แต่โปรดทราบว่าหน่วยความจำหิว

public static boolean areDrawablesIdentical(Drawable drawableA, Drawable drawableB) {
    Drawable.ConstantState stateA = drawableA.getConstantState();
    Drawable.ConstantState stateB = drawableB.getConstantState();
    // If the constant state is identical, they are using the same drawable resource.
    // However, the opposite is not necessarily true.
    return (stateA != null && stateB != null && stateA.equals(stateB))
            || getBitmap(drawableA).sameAs(getBitmap(drawableB));
}

public static Bitmap getBitmap(Drawable drawable) {
    Bitmap result;
    if (drawable instanceof BitmapDrawable) {
        result = ((BitmapDrawable) drawable).getBitmap();
    } else {
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        // Some drawables have no intrinsic width - e.g. solid colours.
        if (width <= 0) {
            width = 1;
        }
        if (height <= 0) {
            height = 1;
        }

        result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(result);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
    }
    return result;
}

นี่เป็นความจริง 100% และต้องการการโหวตเพิ่มขึ้น! ผู้คนโปรดทดสอบโค้ดของคุณด้วย Drawables ที่รู้จักก่อนที่จะทำการgetConstantState()เปรียบเทียบในทันที
avalancha

ผลการวิจัยที่ดี! แต่ตามเอกสารของ BitmapDrawable.getBitmap () getBitmap () อาจเป็นโมฆะดังนั้นคุณอาจต้องการตรวจสอบสิ่งนี้เช่นกัน
PhilLab

คำตอบนี้เป็นจริงและฉันได้ตรวจสอบแล้วว่าหลังจากดีบักหลายชั่วโมงด้วยการเข้ารหัสเฉพาะ getConstantState ()
Raghav Satyadev

เพื่อความปลอดภัยsetBoundsและdrawเป็นสำเนาแทนstackoverflow.com/a/25462223/1916449
arekolek

สิ่งนี้ใช้ไม่ได้กับ BitmapDrawables ที่มีสี (ดู setTintMode / setTint / setTintList) บิตแมปสามารถเป็นไบต์ที่เหมือนกันสำหรับไบต์ แต่มีคุณสมบัติสีต่างกัน เนื่องจาก Android SDK ไม่มีตัวรับสำหรับคุณสมบัติการย้อมสีจึงอาจไม่มีวิธีใดที่จะทำให้ใช้งานได้กับแถบสี
ธีโอ

13

คำถามของฉันคือแค่เปรียบเทียบสอง drawables ฉันลองแล้ว แต่ไม่สามารถหาวิธีใด ๆ ที่เปรียบเทียบสอง drawables ได้โดยตรงอย่างไรก็ตามสำหรับวิธีแก้ปัญหาของฉันฉันเปลี่ยน drawable เป็น bitmap จากนั้นเปรียบเทียบสอง bitmaps และใช้งานได้

Bitmap bitmap = ((BitmapDrawable)fDraw).getBitmap();
Bitmap bitmap2 = ((BitmapDrawable)sDraw).getBitmap();

if(bitmap == bitmap2)
    {
        //Code blcok
    }

นั่นคือสิ่งที่ฉันแนะนำคุณเพื่อเปรียบเทียบที่วาดได้ตามประเภทของ drawable
jeet

1
คุณอาจต้องการเปรียบเทียบบิตแมปเช่นนี้: stackoverflow.com/a/7696320/317889
HGPB

2
สิ่งนี้หนักมากดังนั้นให้พิจารณาการรีไซเคิล Bitmaps ไม่เช่นนั้นคุณจะจบลงด้วย OutOfMemoryError!
paulgavrikov

2
เหตุใดคุณจึงเปรียบเทียบ Bitmaps กับความเท่าเทียมกันของตัวชี้ได้ (==) ฉันคาดว่าจะต้องใช้ Bitmap.equals ()
Ellen Spertus

@espertus คุณพูดถูก ฉันใช้คำถามเดียวกันสำหรับวัตถุที่วาดได้ไม่รู้ว่าทำไมถึงเปลี่ยนเป็น == สำหรับวัตถุบิตแมปในคำตอบ ขอบคุณสำหรับวิธีการใด ๆ ที่ชี้ให้เห็นพื้นฐานนี้
Roshan Jha

9

สำหรับ SDK 21+

ใช้งานได้ใน SDK -21

mRememberPwd.getDrawable().getConstantState().equals
        (getResources().getDrawable(R.drawable.login_checked).getConstantState())

สำหรับ SDK +21 android 5. ตั้งค่า id drawable เป็น imageview พร้อมแท็ก

img.setTag(R.drawable.xxx);

แล้วเปรียบเทียบแบบนี้

if ((Integer) img.getTag() == R.drawable.xxx)
{
....your code
}

วิธีนี้เหมาะสำหรับผู้ที่ต้องการเปรียบเทียบdrawableรหัสของimageviewมี id drawable.xxxของ


อันนี้ใช้ได้จริง แต่แอบตกใจว่าทำไมไม่มีทางเป็นไปได้ T_T!
error1337


4

getDrawable (int)เลิกใช้แล้ว ใช้ getDrawable (บริบท R.drawable.yourimageid)

เพื่อเปรียบเทียบสองพื้นหลัง

Boolean Condition1=v.getBackground().getConstantState().equals(
ContextCompat.getDrawable(getApplicationContext(),R.drawable.***).getConstantState());

2
สิ่งนี้ใช้งานได้เหมือนเป็นเสน่ห์ในการแก้ไขข้อบกพร่องแปลก ๆ บน Android 5 ในโค้ดของฉันสิ่งที่วาดได้จริงถูกส่งคืนมาพร้อมกับcontext.getResources().getDrawable(R.drawable.***)Android 6+ แต่ไม่ได้อยู่ใน Android 5 ด้วยการเปลี่ยนแปลงเล็กน้อยนี้ฉันสามารถเปรียบเทียบสิ่งที่วาดพื้นหลังใน Android ทุกเวอร์ชันได้อย่างไม่มีที่ติ
Jose_GD

3

อาจลองด้วยวิธีนี้:

public void MyClick(View view)
{
 Drawable fDraw = view.getBackground();
 Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);

  if(fDraw.hashCode() == sDraw.hashCode())
  {
   //Not coming
  }
}

หรือเตรียมวิธีการที่ใช้อาร์กิวเมนต์ที่วาดได้สองข้อและส่งคืนบูลีน ในวิธีนั้นคุณสามารถแปลง drawable เป็นไบต์และเปรียบเทียบ

public boolean compareDrawable(Drawable d1, Drawable d2){
    try{
        Bitmap bitmap1 = ((BitmapDrawable)d1).getBitmap();
        ByteArrayOutputStream stream1 = new ByteArrayOutputStream();
        bitmap1.compress(Bitmap.CompressFormat.JPEG, 100, stream1);
        stream1.flush();
        byte[] bitmapdata1 = stream1.toByteArray();
        stream1.close();

        Bitmap bitmap2 = ((BitmapDrawable)d2).getBitmap();
        ByteArrayOutputStream stream2 = new ByteArrayOutputStream();
        bitmap2.compress(Bitmap.CompressFormat.JPEG, 100, stream2);
        stream2.flush();
        byte[] bitmapdata2 = stream2.toByteArray();
        stream2.close();

        return bitmapdata1.equals(bitmapdata2);
    }
    catch (Exception e) {
        // TODO: handle exception
    }
    return false;
}

ตรวจสอบคำตอบที่อัปเดต หากยังไม่ได้ผลให้ตรวจสอบ drawables ของคุณ หรือลองส่ง drawables เดียวกันเพื่อตรวจสอบการทำงานของโค้ด
waqaslam

ใช่หลังจากไม่ได้รับความสำเร็จฉันก็คิดเหมือนกันสำหรับการแปลง drawables เป็น bitmap แล้วเป็น byte ให้ฉันลองดูขอบคุณสำหรับความพยายามของคุณ
Roshan Jha

ไม่ได้ผลคุณทดสอบหรือไม่ อาจจะมีบางอย่างผิดปกติเราไม่สามารถเปรียบเทียบสอง drawables โดยตรง?
Roshan Jha

คุณลองส่งผ่าน drawable เดียวกันสำหรับe.g R.drawable.abcทั้งสองพารามิเตอร์หรือไม่
waqaslam

สวัสดี waqas ตรวจสอบวิธีการของคุณอีกครั้งแล้วบอกอีกครั้งว่าใช้งานได้หรือไม่อาจเป็นไปได้ว่าฉันทำอะไรผิดพลาด แต่ไม่ได้ผล แต่จากนั้นก็เปลี่ยนคำจำกัดความของคำถามของฉันว่าจะเปรียบเทียบสอง drawables ได้อย่างไร วาดเป็นบิตแมปได้จากนั้นไบต์จะเปรียบเทียบบิตแมปและไบต์ที่ไม่ใช่ความต้องการของฉันหากเราตรวจสอบวิธีการวาดแล้วมี method .equals (object) ดังนั้นฉันคิดว่ามันควรจะใช้งานได้โดยตรง แต่ก็ไม่ได้คุณสามารถตรวจสอบคำตอบของฉันได้ ด้านล่างฉันกำลังแปลงไฟล์ที่วาดได้เป็นบิตแมปจากนั้นเปรียบเทียบสองบิตแมปและใช้งานได้
Roshan Jha

2

โอเคฉันคิดว่าฉันเจอทางออกที่ดีที่สุดแล้ว เพราะ AppCompat และเพื่อน drawable getResources().getBitmap(R.drawable.my_awesome_drawable)ที่มีให้บริการในบางครั้งจะสูงขึ้นในรูปแบบที่แตกต่างกันดังนั้นจึงไม่พอที่จะทำ

ดังนั้นเพื่อให้ได้อินสแตนซ์ที่วาดได้ประเภทและรูปแบบเดียวกันตามที่มุมมองให้ไว้สามารถทำได้:

public static Drawable drawableFrom(View view, @DrawableRes int drawableId) {
    Context context = view.getContext();
    try {
        View dummyView = view.getClass().getConstructor(Context.class).newInstance(context);
        dummyView.setBackgroundResource(drawableId);
        return dummyView.getBackground();
    } catch (Exception e) {
      return ResourcesCompat.getDrawable(context.getResources(), drawableId, null);
    }
}

สิ่งนี้มีประโยชน์เมื่อทำการทดสอบ อย่างไรก็ตามฉันไม่แนะนำให้ทำสิ่งนี้ในการผลิต หากคุณต้องการแคชเพิ่มเติมควรหลีกเลี่ยงการสะท้อนแสงมากเกินไป

สำหรับการทดสอบ Expresso คุณสามารถใช้สิ่งนี้ได้ค่อนข้างดี:

onView(withDrawable(R.drawable.awesome_drawable))
  .check(matches(isDisplayed()));

หรือ

onView(withId(R.id.view_id))
  .check(matches(withDrawable(R.drawable.awesome_drawable)));

ก่อนที่คุณจะต้องประกาศคลาสผู้ช่วยนี้:

public class CustomMatchers {

  public static Matcher<View> withDrawable(@DrawableRes final int drawableId) {
     return new DrawableViewMatcher(drawableId);
  }
  private static class DrawableViewMatcher extends TypeSafeMatcher<View> {

     private final int expectedId;
     private String resourceName;

     private enum DrawableExtractionPolicy {
        IMAGE_VIEW {
          @Override
          Drawable findDrawable(View view) {
             return view instanceof ImageView ? ((ImageView) view).getDrawable() : null;
          }
        },
        TEXT_VIEW_COMPOUND {
          @Override
          Drawable findDrawable(View view) {
             return view instanceof TextView ? findFirstCompoundDrawable((TextView) view) : null;
          }
        },
        BACKGROUND {
          @Override
          Drawable findDrawable(View view) {
             return view.getBackground();
          }
        };

        @Nullable
        private static Drawable findFirstCompoundDrawable(TextView view) {
          for (Drawable drawable : view.getCompoundDrawables()) {
             if (drawable != null) {
                return drawable;
             }
          }
          return null;
        }

        abstract Drawable findDrawable(View view);

     }

     private DrawableViewMatcher(@DrawableRes int expectedId) {
        this.expectedId = expectedId;
     }

     @Override
     protected boolean matchesSafely(View view) {
        resourceName = resources(view).getResourceName(expectedId);
        return haveSameState(actualDrawable(view), expectedDrawable(view));
     }

     private boolean haveSameState(Drawable actual, Drawable expected) {
        return actual != null && expected != null && areEqual(expected.getConstantState(), actual.getConstantState());
     }

     private Drawable actualDrawable(View view) {
        for (DrawableExtractionPolicy policy : DrawableExtractionPolicy.values()) {
          Drawable drawable = policy.findDrawable(view);
          if (drawable != null) {
             return drawable;
          }
        }
        return null;
     }

     private boolean areEqual(Object first, Object second) {
        return first == null ? second == null : first.equals(second);
     }

     private Drawable expectedDrawable(View view) {
        return drawableFrom(view, expectedId);
     }

     private static Drawable drawableFrom(View view, @DrawableRes int drawableId) {
        Context context = view.getContext();
        try {
          View dummyView = view.getClass().getConstructor(Context.class).newInstance(context);
          dummyView.setBackgroundResource(drawableId);
          return dummyView.getBackground();
        } catch (Exception e) {
          return ResourcesCompat.getDrawable(context.getResources(), drawableId, null);
        }
     }

     @NonNull
     private Resources resources(View view) {
        return view.getContext().getResources();
     }

     @Override
     public void describeTo(Description description) {
        description.appendText("with drawable from resource id: ");
        description.appendValue(expectedId);
        if (resourceName != null) {
          description.appendValueList("[", "", "]", resourceName);
        }
     }
  }

}


0

ผมตอบไปแล้วในหัวข้อที่คล้ายกันที่นี่: รับ ID ของ drawable ใน ImageView วิธีการที่จะขึ้นอยู่กับการติดแท็กมุมมองที่มี ID LayoutInflaterทรัพยากรที่ระบุไว้ในที่กำหนดเอง กระบวนการทั้งหมดเป็นไปโดยอัตโนมัติโดยห้องสมุดง่ายTagView

ด้วยเหตุนี้คุณสามารถเปรียบเทียบสอง drawables ได้โดยใช้รหัส:

TagViewUtils.getTag(view, ViewTag.VIEW_BACKGROUND.id) == R.drawable.twt_hover

0

การขยายคำตอบจาก @vaughandroid Matcher ต่อไปนี้ใช้ได้กับ Vector Drawable ที่ย้อมสี คุณต้องระบุสีที่ใช้สำหรับ Drawable

public static Matcher<View> compareVectorDrawables(final int imageId, final int tintId) {
        return new TypeSafeMatcher<View>() {

        @Override
        protected boolean matchesSafely(View target) {
            if (!(target instanceof ImageView)) {
                return false;
            }
            ImageView imageView = (ImageView) target;
            if (imageId < 0) {
                return imageView.getDrawable() == null;
            }
            Resources resources = target.getContext().getResources();
            Drawable expectedDrawable = resources.getDrawable(imageId, null);
            if (expectedDrawable == null) {
                return false;
            }

            Drawable imageDrawable = imageView.getDrawable();
            ColorFilter imageColorFilter = imageDrawable.getColorFilter();

            expectedDrawable.setColorFilter(imageColorFilter);
            expectedDrawable.setTintList(target.getResources()
                    .getColorStateList(tintId, null));

            boolean areSame = areDrawablesIdentical(imageDrawable, expectedDrawable);
            return areSame;
        }

        public boolean areDrawablesIdentical(Drawable drawableA, Drawable drawableB) {
            Drawable.ConstantState stateA = drawableA.getConstantState();
            Drawable.ConstantState stateB = drawableB.getConstantState();
            // If the constant state is identical, they are using the same drawable resource.
            // However, the opposite is not necessarily true.
            return (stateA != null && stateB != null && stateA.equals(stateB))
                    || getBitmap(drawableA).sameAs(getBitmap(drawableB));
        }

        public Bitmap getBitmap(Drawable drawable) {
            Bitmap result;
            if (drawable instanceof BitmapDrawable) {
                result = ((BitmapDrawable) drawable).getBitmap();
            } else {
                int width = drawable.getIntrinsicWidth();
                int height = drawable.getIntrinsicHeight();
                // Some drawables have no intrinsic width - e.g. solid colours.
                if (width <= 0) {
                    width = 1;
                }
                if (height <= 0) {
                    height = 1;
                }

                result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(result);
                drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
                drawable.draw(canvas);
            }
            return result;
        }

        @Override
        public void describeTo(Description description) {

        }
    };
}


-1

หากคุณต้องการเปรียบเทียบสองรายการที่วาดได้โดยตรงให้ใช้รหัสต่อไปนี้

fDraw ที่วาดได้ = getResources (). getDrawable (R.drawable.twt_hover);

Drawable sDraw = getResources (). getDrawable (R.drawable.twt_hover);

if (fDraw.getConstantState().equals(sDraw.getConstantState())) {
    //write your code.
} else {
    //write your code.
}

-2

เมื่อคุณใช้equals()วิธีการนี้จะใช้เพื่อเปรียบเทียบเนื้อหา คุณควรลอง==เปรียบเทียบวัตถุสองชิ้น

public void MyClick(View view)
{
 Drawable fDraw = view.getBackground();
 Drawable sDraw = getResources().getDrawable(R.drawable.twt_hover);

  if( fDraw == sDraw )
  {
   // Coming
  }
}

อาจเป็นไปได้ว่าพวกเขาไม่ใช่ == พวกเขาเป็น! =
Lucifer

แต่พวกเขาอ้างถึงภาพเดียวกันจากแหล่งข้อมูล
Roshan Jha

ฉันแค่ต้องตรวจสอบว่าเท่ากันหรือไม่ไม่มีวิธีใดที่สามารถใช้ได้?
Roshan Jha

ใช่ฉันมีความต้องการของคุณโปรดมาที่ห้องสนทนา
Lucifer

1
== เปรียบเทียบว่านี่เป็นวัตถุเดียวกันหรือไม่ซึ่งไม่ใช่ n 99.99999999% ของเวลา
paulgavrikov
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.