gitweixin
  • 首页
  • 小程序代码
    • 资讯读书
    • 工具类
    • O2O
    • 地图定位
    • 社交
    • 行业软件
    • 电商类
    • 互联网类
    • 企业类
    • UI控件
  • 大数据开发
    • Hadoop
    • Spark
    • Hbase
    • Elasticsearch
    • Kafka
    • Flink
    • 数据仓库
    • 数据挖掘
    • flume
    • Kafka
    • Hive
    • shardingsphere
    • solr
  • 开发博客
    • Android
    • php
    • python
    • 运维
    • 技术架构
    • 数据库
  • 程序员网赚
  • bug清单
  • 量化投资
  • 在线查询工具
    • 去行号
    • 在线时间戳转换工具
    • 免费图片批量修改尺寸在线工具
    • SVG转JPG在线工具

月度归档12月 2020

精品微信小程序开发门户,代码全部亲测可用

  • 首页   /  2020   /  
  • 12月
Android 12月 16,2020

Android聊天输入框控件

public class ChatInputWidget extends LinearLayout implements OnClickListener {
    private final String TAG = getClass().getSimpleName();
    private ViewPager mEmojiPager;
    private GifEditText mEditText;
    private ImageView mEmojiImageView;
    private Button mSendButton;
    private View pagerView;
    private CircleIndicator circleIndicator;

    private Handler handler = new Handler();
    private List<Emoji> mEmojiList;

    private static final String FILE_NAME = "emoji.txt";
    private static final String GIFT_RES_FILE_PATH = "emoji";
    private static final String ASSETS_FILE_NAME = GIFT_RES_FILE_PATH + "/" + FILE_NAME;
    private final int pagerNum = 10;

    protected Context mContext;
//    protected InputMethodManager inputManager;

    private ChatInputMenuListener mListener;
    private int mToUserId;
    private int mToVipLevel;
    private String toUserName;

    public String getToUserName() {
        return toUserName;
    }

    public void setToUserName(String toUserName) {
        this.toUserName = toUserName;
    }

    public void setToVipLevel(int toVipLevel) {
        this.mToVipLevel = toVipLevel;
    }

    public ChatInputWidget(Context context) {
        super(context);
        if (context instanceof Activity) {
            ((Activity) context).getLayoutInflater().inflate(R.layout.widget_chat_input, this);
        } else {
            inflate(context, R.layout.widget_chat_input, this);
        }
        initView(context);
    }

    public ChatInputWidget(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (context instanceof Activity) {
            ((Activity) context).getLayoutInflater().inflate(R.layout.widget_chat_input, this);
        } else {
            inflate(context, R.layout.widget_chat_input, this);
        }
        initView(context);
    }

    private void initView(Context context) {
        this.mContext = context;
//        inputManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);

        mEditText = (GifEditText) findViewById(R.id.chat_et_message);
        mEmojiPager = (ViewPager) findViewById(R.id.chat_vp_face_pager);
        mEmojiImageView = (ImageView) findViewById(R.id.chat_iv_face);
        mSendButton = (Button) findViewById(R.id.chat_bt_send);
        pagerView = findViewById(R.id.rl_chat_pager);
        circleIndicator = (CircleIndicator) findViewById(R.id.indicator_chat_emoji);

    }

    public void init() {
        mSendButton.setOnClickListener(this);
        mEmojiImageView.setOnClickListener(this);
        mEditText.setOnClickListener(this);
        mEditText.setOnFocusChangeListener(new OnFocusChangeListener() {

            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    hideEmojicon();
                }
            }
        });

        mEmojiList = getExpressionRes();
        List<View> views = new ArrayList<>();

        int pagerSize;

        if (mEmojiList.size() % pagerNum == 0) {
            pagerSize = mEmojiList.size() / pagerNum;
        } else {
            pagerSize = mEmojiList.size() / pagerNum + 1;
        }

//        LogUtil.w(TAG, "pagerSize === " + pagerSize);

        for (int i = 0; i < pagerSize; i++) {
            if (i == pagerSize - 1) {
                views.add(getGridChildView(mEmojiList.subList(i * pagerNum, mEmojiList.size())));
            } else {
                views.add(getGridChildView(mEmojiList.subList(i * pagerNum, (i + 1) * pagerNum)));
            }
        }
        mEmojiPager.setAdapter(new EmojiPagerAdapter(views));
        circleIndicator.setViewPager(mEmojiPager);
    }

    /**
     * 显示或隐藏表情页
     */
    private void toggleEmojicon() {
        if (pagerView.getVisibility() == View.GONE) {
            hideKeyboard();
            handler.postDelayed(new Runnable() {
                public void run() {
                    showEmojicon();
                }
            }, 100);
        } else {
            hideEmojicon();
            handler.postDelayed(new Runnable() {
                public void run() {
                    showKeyboard();
                }
            }, 100);
        }
    }

    /**
     * 隐藏表情页
     */
    public void hideEmojicon() {
        pagerView.setVisibility(View.GONE);
        mEmojiImageView.setSelected(false);
    }

    private void showEmojicon() {
        pagerView.setVisibility(View.VISIBLE);
        mEmojiImageView.setSelected(true);
    }

    /**
     * 隐藏表情按钮
     */
    public void hideEmojiButton() {
        mEmojiImageView.setVisibility(View.GONE);
    }

    public void setHint(String hint) {
        mEditText.setHint(hint);
    }

    public void setHint(int resid) {
        mEditText.setHint(resid);
    }

    public String getText() {
        return mEditText.getText().toString();
    }

    public void setText(String text) {
        mEditText.setText(text);
        if (text != null) {    //光标移到最后
            mEditText.setSelection(text.length());
        }
    }

    public void setText(int resid) {
        mEditText.setText(resid);
    }

    public void setFocus() {
        mEditText.setFocusable(true);
        mEditText.setFocusableInTouchMode(true);
        mEditText.requestFocus();
        mEditText.requestFocusFromTouch();
        mEditText.postDelayed(new Runnable() {
            @Override
            public void run() {
                AppUtil.showInputMethod(mEditText);
            }
        }, 50);
    }

    public View getEditText() {
        return mEditText;
    }

    /**
     * 隐藏软键盘
     */
    public void hideKeyboard() {
        AppUtil.hideInputMethod(mEditText);
    }

    public void showKeyboard() {
        AppUtil.showInputMethod(mEditText);
    }

    /**
     * 系统返回键被按时调用此方法
     *
     * @return 返回false表示返回键时扩展菜单栏时打开状态,true则表示按返回键时扩展栏是关闭状态<br/>
     * 如果返回时打开状态状态,会先关闭扩展栏再返回值
     */
    public boolean onBackPressed() {
        if (pagerView.getVisibility() == View.VISIBLE) {    //隐藏表情
            hideEmojicon();
            return false;
        } else {
            return true;
        }

    }

    private void sendMessage() {
        //通过回调通知发送消息
        if (mListener != null) {
            String message = StrUtil.escapeForXML(mEditText.getText().toString());
            if (mListener.onSendMessage(message, mToUserId, mToVipLevel, toUserName)) {
                //清空内容
                mEditText.setText(null);
            }
        }
    }

    public void setToUserId(int userId) {
        mToUserId = userId;
    }

    public int getToUserId() {
        return mToUserId;
    }

    /**
     * 清除输入框内容
     */
    public void clearText() {
        mEditText.setText(null);
    }

    public void setChatInputMenuListener(ChatInputMenuListener listener) {
        this.mListener = listener;
    }

    private void insertEmoji(String name) {
        final int pos = mEditText.getSelectionStart();
        final String faceString = "[$" + name + "$]";
        final Editable text = (Editable) mEditText.getText();
        text.insert(pos, faceString);
        final GifDrawable drawable = EmojiHelper.getInstance().getGifDrawable(name);
        if (drawable != null) {
            final ImageSpan gifSpan = new GifImageSpan(drawable);
            text.setSpan(gifSpan, pos, pos + faceString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }

    private List<Emoji> getExpressionRes() {
        List<Emoji> reslist = new ArrayList<>();
        String emojiJson = FileUtil.readAssetsByName(mContext, ASSETS_FILE_NAME);

        try {
            reslist = parseGiftList(new JSONObject(emojiJson));
        } catch (Exception e) {
            LogUtil.e(TAG, "read local gift error:" + e.toString());
        }
        return reslist;
    }

    private List<Emoji> parseGiftList(JSONObject json) throws Exception {
        List<Emoji> giftList = new ArrayList<>();
        JSONArray jsonArray = json.getJSONArray("emoji");
        if (jsonArray != null) {
            for (int i = 0; i < jsonArray.length(); i++) {
                giftList.add(parseGift(jsonArray.getJSONObject(i)));
            }
        }
        return giftList;
    }

    private Emoji parseGift(JSONObject json) throws Exception {
        Emoji emoji = new Emoji();
        emoji.setId(json.getString("id"));
        emoji.setName(json.getString("name"));
        return emoji;
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.chat_iv_face:
                toggleEmojicon();
                break;
            case R.id.chat_et_message:
                hideEmojicon();
                break;
            case R.id.chat_bt_send:
                sendMessage();
                break;
        }
    }

    /**
     * 获取表情的gridview的子view
     *
     * @param
     * @return
     */
    private View getGridChildView(List<Emoji> list) {
        final EmojiGridAdapter expressionAdapter = new EmojiGridAdapter(mContext, list);
        GridView gridView = (GridView) LayoutInflater.from(mContext).inflate(R.layout.layout_emoji_grid, null);
        gridView.setAdapter(expressionAdapter);
        gridView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                try {
                    // 文字输入框可见时,才可输入表情
                    if (mEditText.getVisibility() == View.VISIBLE) {
                        insertEmoji(expressionAdapter.getItem(position).getId());
                    }
                } catch (Exception e) {
                }

            }
        });
        return gridView;
    }

    public class EmojiPagerAdapter extends PagerAdapter {

        private List<View> views;

        public EmojiPagerAdapter(List<View> views) {
            this.views = views;
        }

        @Override
        public int getCount() {
            return views.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ((ViewPager) container).addView(views.get(position));
            return views.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            ((ViewPager) container).removeView(views.get(position));

        }

    }

    private class EmojiGridAdapter extends QuickAdapter<Emoji> {

        public EmojiGridAdapter(Context context, List<Emoji> objects) {
            super(context, R.layout.item_emoji, objects);
        }

        @Override
        protected void convert(BaseAdapterHelper helper, Emoji item) {
            helper.setText(R.id.emoji_tv_expression, item.getName());
            helper.setImageDrawable(R.id.emoji_iv_expression, EmojiHelper.getInstance().getPngDrawable(item.getId()));
        }

    }

    public int getChatInputLength() {
        if (null != mEditText) {
            return mEditText.getChatInputLength();
        } else {
            return 0;
        }
    }

    public interface ChatInputMenuListener {
        /**
         * 发送消息按钮点击
         *
         * @param content  文本内容
         * @param toUserId
         */
        boolean onSendMessage(String content, int toUserId, int toVipLevel, String toUserName);

    }

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              xmlns:skin="http://schemas.android.com/android/skin"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:background="@drawable/skin_label_gray_bg"
              android:orientation="vertical"
              skin:enable="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:gravity="center_vertical|right"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1.00"
            android:background="@drawable/skin_label_white_bg"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:paddingBottom="8dp"
            android:paddingRight="5dp"
            android:paddingTop="8dp"
            skin:enable="true">

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1.00"
                android:gravity="center_vertical|right"
                android:orientation="horizontal">

                <!-- <LinearLayout
                    android:id="@+id/chat_ll_to"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal" >

                    <TextView
                        android:id="@+id/chat_tv_to"
                        android:layout_width="60dp"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_vertical"
                        android:layout_marginLeft="5dp"
                        android:drawablePadding="5dp"
                        android:drawableRight="@drawable/small_up_arrow"
                        android:ellipsize="end"
                        android:gravity="right"
                        android:singleLine="true"
                        android:text="@string/chat_text_toall"
                        android:textColor="@color/common_blue"
                        android:textSize="14sp" />

                    <View
                        android:layout_width="1px"
                        android:layout_height="match_parent"
                        android:layout_marginLeft="5dp"
                        android:background="@color/line_view_item" />
                </LinearLayout> -->

                <com.gift.view.GifEditText
                    android:id="@+id/chat_et_message"
                    style="?android:attr/editTextStyle"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:background="@null"
                    android:focusable="true"
                    android:focusableInTouchMode="true"
                    android:gravity="center_vertical"
                    android:hint="@string/chat_hint_message"
                    android:imeOptions="actionNone"
                    android:inputType="textMultiLine"
                    android:maxHeight="100dp"
                    android:maxLength="200"
                    android:textColor="@color/text_content"
                    android:textColorHint="@color/text_prompt_input"
                    android:textSize="14sp"/>
            </LinearLayout>

            <ImageView
                android:id="@+id/chat_iv_face"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="5dp"
                android:src="@drawable/button_selector_chat_face"/>
        </LinearLayout>

        <Button
            android:id="@+id/chat_bt_send"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginLeft="5dp"
            android:background="@drawable/skin_button_bg_white"
            android:paddingLeft="15dp"
            android:paddingRight="15dp"
            android:text="@string/chat_text_send"
            android:textColor="@color/button_text_color"
            android:textSize="14sp"
            skin:enable="true"/>
    </LinearLayout>

    <RelativeLayout
        android:id="@+id/rl_chat_pager"
        android:layout_width="match_parent"
        android:layout_height="270dp"
        android:background="@color/white"
        android:paddingBottom="27dp"
        android:visibility="gone"
        >

        <android.support.v4.view.ViewPager
            android:id="@+id/chat_vp_face_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:persistentDrawingCache="animation"
            />

        <com.gift.view.infiniteview.CircleIndicator
            android:id="@+id/indicator_chat_emoji"
            android:layout_width="match_parent"
            android:layout_height="6dp"
            android:layout_alignParentBottom="true"
            app:ci_background="@color/banner_indicator_selected"
            app:ci_gravity="center"
            app:ci_margin="5dp"
            app:ci_mode="solo"
            app:ci_radius="3dp"
            app:ci_selected_background="@color/text_orange"
            />

    </RelativeLayout>

</LinearLayout>
作者 east
Android 12月 16,2020

Android直播间刷礼物动画控件

public class GiftWidget extends LinearLayout implements OnClickListener, GiftNumberChangeListener {
    private ViewPager mViewPager;
    private Button mGiveButton;
    private View mNumberView;
    private TextView mBalanceTextView;
    private TextView mNumberTextView;
    private LinearLayout mIndicatorView;
    private TextView pay;

    private final static int GIFT_MAX_ITEM_SIZE = 10;
    private int indicatorSpace = 5;
    private int mNumber;
    private String mSelectedGiftId;

    private GiftNumberPopup mGiftNumberPopup;
    private List<GiftGridAdapter> mAdapterList = new ArrayList<GiftGridAdapter>();

    private GiftGiveClickListener mGiftGiveListener;
    private Context mContext;

    public GiftWidget(Context context) {
        super(context);
        if (context instanceof Activity) {
            ((Activity) context).getLayoutInflater()
                    .inflate(R.layout.widget_gift, this);
        } else {
            inflate(context, R.layout.widget_gift, this);
        }
        initView(context);
    }

    public GiftWidget(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (context instanceof Activity) {
            ((Activity) context).getLayoutInflater()
                    .inflate(R.layout.widget_gift, this);
        } else {
            inflate(context, R.layout.widget_gift, this);
        }
        initView(context);
    }

    private void initView(Context context) {
        this.mContext = context;
        mViewPager = (ViewPager) findViewById(R.id.gift_vp_pager);
        mGiveButton = (Button) findViewById(R.id.gift_bt_give);
        mNumberView = findViewById(R.id.gift_ll_number);
        mBalanceTextView = (TextView) findViewById(R.id.gift_tv_balance);
        mNumberTextView = (TextView) findViewById(R.id.gift_tv_number);
        mIndicatorView = (LinearLayout) findViewById(R.id.gift_ll_indicator);
        pay = (TextView) findViewById(R.id.gift_tv_balance_cz);
        mNumberView.setOnClickListener(this);
        mGiveButton.setOnClickListener(this);
        pay.setOnClickListener(this);
    }

    public void initGift(List<Gift> giftList) {
        if (giftList == null) {
            return;
        }
        mAdapterList.clear();
        if (giftList.size() > 0 && mSelectedGiftId == null) {    //默认选中第一个礼物
            mSelectedGiftId = giftList.get(0).getId();
        }
        final List<View> views = new ArrayList<View>();
        int start = 0;
        while (start < giftList.size()) {
            int count;
            if (giftList.size() - start > GIFT_MAX_ITEM_SIZE) {
                count = GIFT_MAX_ITEM_SIZE;
            } else {
                count = giftList.size() - start;
            }
            List<Gift> list = giftList.subList(start, start + count);
            start = start + count;
            views.add(getGridChildView(list));
        }
        initIndicator(views.size());

        mViewPager.setAdapter(new GiftPagerAdapter(views));
        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                switchIndicator(position);
                //刷新旧的选中状态
                if (mAdapterList.size() > position) {
                    GiftGridAdapter adapter = mAdapterList.get(position);
                    adapter.notifyDataSetChanged();
                }
            }
        });
        switchIndicator(0);
    }

    private void initIndicator(int count) {
        mIndicatorView.removeAllViews();
        for (int i = 0; i < count; i++) {
            ImageView indicator = new ImageView(getContext());
            indicator.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            indicator.setPadding(indicatorSpace, indicatorSpace, indicatorSpace, indicatorSpace);
            indicator.setImageResource(R.drawable.icon_indicator);
            mIndicatorView.addView(indicator);
        }
    }

    private void switchIndicator(int position) {
        for (int i = 0; i < mIndicatorView.getChildCount(); i++) {
            ((ImageView) mIndicatorView.getChildAt(i)).setSelected(i == position ? true : false);
        }
    }

    private View getGridChildView(List<Gift> giftList) {
        final GiftGridAdapter adapter = new GiftGridAdapter(getContext(), 0, giftList);
        mAdapterList.add(adapter);
        GridView gridView = (GridView) LayoutInflater.from(getContext()).inflate(R.layout.layout_gift_grid, null);
        gridView.setAdapter(adapter);
        gridView.setChoiceMode(GridView.CHOICE_MODE_SINGLE);
        gridView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                mSelectedGiftId = adapter.getItem(position).getId();
                adapter.notifyDataSetChanged();
            }
        });
        return gridView;
    }

    public class GiftPagerAdapter extends PagerAdapter {

        private List<View> views;

        public GiftPagerAdapter(List<View> views) {
            this.views = views;
        }

        @Override
        public int getCount() {
            return views.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ((ViewPager) container).addView(views.get(position));
            return views.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            ((ViewPager) container).removeView(views.get(position));

        }

    }

    private class GiftGridAdapter extends ArrayAdapter<Gift> {

        public GiftGridAdapter(Context context, int textViewResourceId, List<Gift> giftList) {
            super(context, textViewResourceId, giftList);
        }


        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if (convertView == null) {
                if (getContext() instanceof Activity) {
                    convertView = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.item_gift, null);
                } else {
                    convertView = View.inflate(getContext(), R.layout.item_gift, null);
                }
                holder = new ViewHolder();
                holder.root = convertView.findViewById(R.id.gift_item_ll_root);
                holder.imageView = (ImageView) convertView.findViewById(R.id.gift_item_iv_gift);
                holder.nameTextView = (TextView) convertView.findViewById(R.id.gift_item_tv_name);
                holder.priceTextView = (TextView) convertView.findViewById(R.id.gift_item_tv_price);
                holder.line = convertView.findViewById(R.id.line);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }

            if (position > 4) {
                holder.line.setVisibility(View.GONE);
            } else {
                holder.line.setVisibility(View.VISIBLE);
            }
            Gift gift = getItem(position);
            try {
                GiftHelper.getInstance().setPngDrawable(holder.imageView, gift);
                holder.nameTextView.setText(gift.getName());
                holder.priceTextView.setText(gift.getPrice());
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (gift.getId().equals(mSelectedGiftId)) {    //选中
                holder.root.setSelected(true);
            } else {
                holder.root.setSelected(false);
            }

            return convertView;
        }

        private class ViewHolder {
            View root;
            View line;
            ImageView imageView;
            TextView nameTextView;
            TextView priceTextView;
        }
    }

    public void initGiftNumber(Activity activity) {
        if (mGiftNumberPopup == null) {
            mGiftNumberPopup = new GiftNumberPopup(activity);
            mGiftNumberPopup.setNumberChangeListener(this);
        }
        //默认显示数量1
        setGiftNumber(1);
    }

    private void showGiftNumber(View view) {
        mGiftNumberPopup.showPopupWindow(view);
    }

    public void dismissGiftNumber() {
        mGiftNumberPopup.dismiss();
    }

    public void setBalance(String text) {
        mBalanceTextView.setText(text);
    }

    private void setGiftNumber(int number) {
        mNumber = number;
        mNumberTextView.setText(String.valueOf(number));
    }

    public void setGiveGift(OnClickListener listener) {
        mGiveButton.setOnClickListener(listener);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.gift_bt_give:
                if (mGiftGiveListener != null && mSelectedGiftId != null) {
                    mGiftGiveListener.onGiftGiveClick(Integer.parseInt(mSelectedGiftId), mNumber);
                }
                break;
            case R.id.gift_ll_number:
                showGiftNumber(v);
                break;
            case R.id.gift_tv_balance_cz:
                RechargeActivity.startActivity(mContext);
                mGiftGiveListener.dismiss();
                break;
        }
    }

    @Override
    public void onGiftNumberChange(int number) {
        setGiftNumber(number);
    }

    public void setOnGiftGiveClickListener(GiftGiveClickListener listener) {
        mGiftGiveListener = listener;
    }

    public interface GiftGiveClickListener {
        public void onGiftGiveClick(int giftId, int number);

        public void dismiss();
    }
}
<?xml version="1.0" encoding="utf-8"?>
<!-- 礼物控件layout -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:skin="http://schemas.android.com/android/skin"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical">


    <com.giflist.view.AdjustHeightViewPager
        android:id="@+id/gift_vp_pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:flipInterval="300"
        android:persistentDrawingCache="animation"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="5dp"
        android:orientation="vertical">

        <LinearLayout
            android:id="@+id/gift_ll_indicator"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="horizontal"/>

        <View
            android:layout_width="match_parent"
            android:layout_height="2px"
            android:layout_marginTop="7dp"
            android:background="@color/line_list"
            skin:enable="true"/>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp">

            <TextView
                android:id="@+id/gift_tv_balance_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:text="@string/gift_text_balance"
                android:textColor="@color/text_content"
                android:textSize="14sp"/>

            <TextView
                android:id="@+id/gift_tv_balance"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginRight="5dp"
                android:layout_toLeftOf="@+id/gift_tv_balance_cz"
                android:layout_toRightOf="@+id/gift_tv_balance_label"
                android:ellipsize="end"
                android:singleLine="true"
                android:textColor="@color/text_content"
                android:textSize="14sp"/>

            <TextView
                android:id="@+id/gift_tv_balance_cz"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginRight="@dimen/top_margin"
                android:layout_toLeftOf="@+id/gift_ll_number"
                android:text="@string/recharge_title"
                android:textColor="@color/skin_selector_text_color"
                android:textSize="14sp"
                skin:enable="true"/>


            <LinearLayout
                android:id="@+id/gift_ll_number"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_centerVertical="true"
                android:paddingBottom="10dp"
                android:paddingTop="10dp"
                android:layout_marginRight="10dp"
                android:layout_toLeftOf="@+id/gift_bt_give"
                android:background="@drawable/button_gray_selector"
                android:clickable="true"
                android:gravity="right|center_vertical"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/gift_tv_number"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/skin_selector_text_color"
                    android:textSize="14sp"
                    android:minWidth="50dp"
                    android:gravity="center"
                    android:text="1"
                    skin:enable="true"/>
                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:paddingRight="10dp"
                    android:src="@drawable/video_present_number_point_skin_n"/>
            </LinearLayout>

            <Button
                android:id="@+id/gift_bt_give"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:background="@drawable/skin_button_orange_selector"
                android:paddingBottom="10dp"
                android:paddingLeft="25dp"
                android:paddingRight="25dp"
                android:paddingTop="10dp"
                android:text="@string/gift_text_give"
                android:textColor="@color/white"
                skin:enable="true"/>
        </RelativeLayout>
    </LinearLayout>

</LinearLayout>
作者 east
Android 12月 11,2020

Android自定义交易密码框


/**
 * 交易密码弹出框
 * Created by Administrator on 2016/4/18.
 */
public class TradePasswordPopup extends BasePopupWindow {

    private Button popupSure, popcancel;
    private MyGridPasswordView passwordEdit;
    private LinearLayout tip_text_layout;
    private TextView incorrect_password;
    private OnFocus onFocus;
    private OnClickListener mListener;

    public TradePasswordPopup(Activity context) {
        super(context, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        mGravity = Gravity.CENTER;
    }

    protected boolean getFocusable() {
        return true;
    }

    @Override
    protected void initView(Activity context) {
        popupSure = (Button) mPopupView.findViewById(R.id.room_password_ok);
        popcancel = (Button) mPopupView.findViewById(R.id.room_password_cancel);
        passwordEdit = (MyGridPasswordView) mPopupView.findViewById(R.id.password_edit);
        incorrect_password = (TextView) mPopupView.findViewById(R.id.tx_tradepassword_error);
        tip_text_layout = (LinearLayout) mPopupView.findViewById(R.id.passworld_edit);
      /*  incorrect_password.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG);
        incorrect_password.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mListener != null) {
                    mListener.onClick(incorrect_password);
                }
                dismiss();
            }
        }); */
        popupSure.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                onFocus.focus(passwordEdit.getPassWord());
            }
        });
        popcancel.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
               dismiss();
            }
        });
        passwordEdit.clearPassword();
        passwordEdit.forceInputViewGetFocus();
        showInputMethod(passwordEdit.getInputView(), 100);
    }

    public void showPopupWindow() {
       super.showPopupWindow();

    }

    public void clearPassword(){
        passwordEdit.clearPassword();
    }

    public void setAskPasswordListener(OnClickListener listener) {
        mListener = listener;
    }

    public void setListener(OnFocus onFocus) {
        this.onFocus = onFocus;
    }

    public interface OnFocus {
        void focus(String txt);
    }

    @Override
    protected Animation getShowAnimation() {
        return null;
    }

    @Override
    protected View getClickToDismissView() {
        return null;
    }

    @Override
    public View getPopupView() {
        return getPopupViewById(R.layout.popup_trade_password_view);
    }

    @Override
    public View getAnimaView() {
        return mPopupView;
    }

    @Override
    public boolean getPopupTransparent() {
        return false;
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/passworld_edit"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="bottom"
    android:gravity="center"
    android:orientation="vertical">


    <LinearLayout

        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/popup_dialog_bg"
        android:gravity="center"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
        android:orientation="vertical"
        android:visibility="visible">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Button
                android:id="@+id/room_password_cancel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:background="@drawable/white_gray_bottomleft_radius"
                android:paddingBottom="20dp"
                android:paddingTop="20dp"
                android:text="@string/cancel"
                android:textColor="@color/text_b2"
                android:textSize="16sp" />

            <TextView
                android:id="@+id/room_password_prompt_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:gravity="center"
                android:padding="10dp"
                android:text="@string/trade_input_trade_password"
                android:textColor="#343434"
                android:textSize="16dp" />


            <Button
                android:id="@+id/room_password_ok"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:background="@drawable/white_gray_bottomright_radius"
                android:paddingBottom="20dp"
                android:paddingTop="20dp"
                android:text="@string/ok"
                android:textColor="@color/bg_title"
                android:textSize="16sp" />
        </RelativeLayout>

        <com.bitvf.bitcoinWallet.view.MyGridPasswordView
            android:id="@+id/password_edit"
            android:layout_width="fill_parent"
            android:layout_height="50dip"
            android:layout_gravity="center_horizontal"
            android:layout_marginBottom="@dimen/verify_code_edit_margin_bottom"
            android:layout_marginLeft="@dimen/verify_code_margin"
            android:layout_marginRight="@dimen/verify_code_margin"
            android:layout_marginTop="30dp"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:gravity="center"
            app:gpvLineColor="@color/divive_line"
            app:gpvLineWidth="@dimen/verify_code_border_width"
            app:gpvPasswordLength="6"
            app:gpvPasswordTransformation="●"
            app:gpvPasswordType="textPassword"
            app:gpvTextSize="18sp" />

        <TextView
            android:id="@+id/tx_tradepassword_error"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dip"
            android:textSize="14sp"
            android:layout_marginTop="5dip"
            android:layout_marginBottom="40dip"
            android:textColor="@color/text_warn"
            tools:text="密码错误,请重新输入"
            />


    </LinearLayout>
</LinearLayout>
作者 east
Android 12月 11,2020

Android记录文件日志工具类



import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.os.StatFs;
import android.util.Log;

import com.bitvf.bitcoinWallet.base.JJApplication;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class FileLogger {
	private static final String TAG = "FileLogger";
	private static final long MB = 1024 * 1024;
	private static final long LOG_LIMIT = 1 * 1024 * 1024;
	private static final long FREE_SPACE_LIMIT = 5 * 1024 * 1024;
	private static final long FREE_SPACE_TIMER = 20 * 60 * 1000;
	private static final int DAY_LIMIT = 3;	//日志保存的天数
	private static final String INFO_COLOR = "green";
	private static final String ERROR_COLOR = "red";
	private static final String WARN_COLOR = "orange";
	private static final String DEBUG_COLOR = "blue";
	private ExecutorService singleThreadService = Executors.newSingleThreadExecutor();
	private Timer timer = null;
	private boolean spaceAvailable = true;

	void d(String tag, String msg) {
		startThreadService(DEBUG_COLOR, "[" + tag + "]" + msg);
	}

	void e(String tag, String msg) {
		startThreadService(ERROR_COLOR, "[" + tag + "]" + "[ERROR]" + msg);
	}

	void i(String tag, String msg) {
		startThreadService(INFO_COLOR, "[" + tag + "]" + msg);
	}

	void w(String tag, String msg) {
		startThreadService(WARN_COLOR, "[" + tag + "]" + "[WARN]" + msg);
	}

	void v(String tag, String msg) {
		startThreadService(INFO_COLOR, "[" + tag + "]" + msg);
	}

	private void startThreadService(String tag, String msg) {
		File file = getLogRoot();
		if ((file == null) || (!file.exists())) {
			return;
		}
		singleThreadService.execute(getWriterRunnable(tag, msg));
	}

	private Runnable getWriterRunnable(final String tag, final String msg) {
		Runnable runnable = new Runnable() {
			public void run() {
				try {
					File file = getLogRoot();
					if ((file == null) || (!file.exists())) {
						return;
					}
					if ((timer == null) && (!freeSpace())) {
						return;
					}
					FileLogger.this.startCleanUpTimer();
					File availableFile = FileLogger.this.getAvailableFile();
					if (availableFile == null) {
						return;
					}
					boolean available = false;
					if (!availableFile.exists()) {
						try {
							availableFile.createNewFile();
							available = true;
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
					if (!availableFile.exists()) {
						return;
					}
					FileOutputStream fileOutputStream = null;
					try {
						fileOutputStream = new FileOutputStream(availableFile, true);
						if (available) {
							String header = "<header>"
									+ "<meta http-equiv=" + "\""
									+ "Content-Type" + "\"" + " content="
									+ "\"" + "text/html; charset=UTF-8" + "\">"
									+ "</header>";
							fileOutputStream.write(header.getBytes());
						}
						SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
						String strDate = simpleDateFormat.format(new Date());
						String body = "<p><font color =\"" + tag + "\">" + strDate + " " + msg.replaceAll(">", ">").replaceAll("<", "<") + "</p>";
						fileOutputStream.write(body.getBytes());
					} catch (FileNotFoundException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					} finally {
						try {
							if (fileOutputStream != null) {
								fileOutputStream.close();
							}
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
					return;
				} catch (NullPointerException e) {
					e.printStackTrace();
					if(null != e) {
						Log.e(TAG, e.getMessage());
					}
				} catch (Exception e) {
					e.printStackTrace();
					if(null != e) {
						Log.e(TAG, e.getMessage());
					}
				}
			}
		};
		return runnable;
	}

	private File getAvailableFile() {
		File rootFile = getLogRoot();
		if ((rootFile == null) || (!rootFile.exists())) {
			return null;
		}
		removeOldFolders();
		File file = getLogFolder();
		int index = 0;
		List<File> fileList = null;
		File[] files = file.listFiles();
		if ((files != null) && (files.length > 0)) {
			fileList = Arrays.asList(files);
			if (fileList.size() > 1) {
				getSortedFileListByName(fileList);
				files = (File[]) fileList.toArray();
			}
			String name = files[0].getName();
			String cntName = name.substring(0, name.indexOf("."));
			try {
				index = Integer.parseInt(cntName);
			} catch (Exception e) {
				e.printStackTrace();
				Log.e(TAG, "Wrong cntName! : " + cntName);
			}
			if (files[0].length() >= MB / 2) {
				index++;
			}
		}
		String fileName = getLogFileName(index);
		return new File(file, fileName);
	}

	private static String getLogFileName(int index) {
		return String.format("%03d", index) + ".html";
	}

	private File getLogFolder() {
		File rootFile = getLogRoot();
		File file = new File(rootFile, getCurrentDate());
		if (!file.exists()) {
			file.mkdirs();
		}
		return file;
	}

	private void removeFolderBeforeDay(String strDate, int offset) {
		String strBeforeDate = getSpecifiedDayBefore(strDate, offset);
		File rootFile = getLogRoot();
		File file = new File(rootFile, strBeforeDate);
		if (file.exists()) {
			deleteFile(file);
		}
	}

	private void removeOldFolders() {
		File file = getLogRoot();
		if ((file == null) || (!file.exists())) {
			return;
		}
		String strDate = getCurrentDate();
		String strYesterdayDate = getSpecifiedDayBefore(strDate, -1);
		String strBeforeYesterdayDate = getSpecifiedDayBefore(strDate, -2);
		File[] files = file.listFiles();
		if (files == null) {
			return;
		}
		for (File subFile : files) {
			if (subFile.isDirectory()) {
				String fileName = subFile.getName();
				if (!fileName.contains(strDate) && !fileName.contains(strYesterdayDate) && !fileName.contains(strBeforeYesterdayDate)) {
					deleteFile(subFile);
				}
			} else {
				subFile.delete();
			}
		}
	}

	private static void deleteFile(File file) {
		if (file == null) {
			return;
		}
		if (file.exists()) {
			if (file.isDirectory()) {
				File[] files = file.listFiles();
				if (files != null) {
					for (File subFile : files) {
						deleteFile(subFile);
					}
				}
				file.delete();
			} else {
				file.delete();
			}
		}
	}

	private static long getDirSize(File floder) {
		if (floder == null) {
			return 0L;
		}
		if (floder.isDirectory()) {
			long size = 0L;
			File[] files = floder.listFiles();
			if (files != null) {
				for (File subFile : files) {
					size += getDirSize(subFile);
				}
			}
			return size;
		}
		return floder.length();
	}

	private static String getSpecifiedDayBefore(String strDate, int offset) {
		Calendar calendar = Calendar.getInstance();
		Date date = null;
		try {
			date = new SimpleDateFormat("yyyyMMdd").parse(strDate);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		calendar.setTime(date);
		calendar.add(Calendar.DATE, offset);
		String dateTime = new SimpleDateFormat("yyyyMMdd").format(calendar.getTime());
		return dateTime;
	}

	private static File getStorageDir() {
		if (Environment.getExternalStorageState().equals("mounted")) {
			return Environment.getExternalStorageDirectory();
		}
		return Environment.getDataDirectory();
	}

	private static void getSortedFileListByName(List<File> fileList) {
		Collections.sort(fileList, new Comparator<File>() {
			public int compare(File lhs, File rhs) {
				return lhs.getName().compareTo(rhs.getName());
			}
		});
	}

	private boolean spaceIsAlearting() {
		return getCurrentAvailabeSpace() < FREE_SPACE_LIMIT;
	}

	private boolean logSizeAlearting() {
		return getDirSize(getLogRoot()) > LOG_LIMIT;
	}

	boolean freeSpace() {
		File file = getLogRoot();
		if ((file == null) || (!file.exists())) {
			return false;
		}
		if (spaceIsAlearting()) {
			Log.w(TAG, "there is no availabe free space and try to free space");
			freeLogFolder();
			return !spaceIsAlearting();
		}
		checkAndFreeLogFiles();
		return true;
	}

	private void freeLogFolder() {
		deleteFile(getLogRoot());
	}

	private void freeOldFolders() {
		File[] files = getLogRoot().listFiles();
		for (File file : files) {
			if ((file.isDirectory()) && (!file.getName().contains(getCurrentDate()))) {
				deleteFile(file);
			} else {
				file.delete();
			}
		}
	}
	
	private String getCurrentDate() {
		return new SimpleDateFormat("yyyyMMdd").format(new Date());
	}

	private void freeOldFiles() {
		File[] files = getLogFolder().listFiles();
		if (files != null) {
			List<File> fileList = Arrays.asList(files);
			getSortedFileListByName(fileList);
			if (fileList.size() > 5) {
				int i = fileList.size();
				for (int j = 5; j < i; j++) {
					Log.w(TAG, "try to delete file : " + fileList.get(j).getAbsoluteFile());
					fileList.get(j).delete();
				}
			}
		}
	}

	@TargetApi(18)
	private static long getCurrentAvailabeSpace() {
		StatFs statFs = new StatFs(getStorageDir().getPath());
		if (Build.VERSION.SDK_INT >= 18) {
			return statFs.getAvailableBytes();
		}
		long availableBlocks = statFs.getAvailableBlocks();
		long blockSize = statFs.getBlockSize();
		return availableBlocks * blockSize;
	}

	File getLogRoot() {
		try {
			Context context = JJApplication.getInstance();
			String filePath = "/Android/data/" + context.getPackageName() + "/log/";
			File file = new File(getStorageDir(), filePath);
			synchronized (this) {
				if (!file.exists()) {
					file.mkdirs();
				}
			}
			return file;
		} catch (Throwable e) {
			return null;
		}
	}

	void checkAndFreeLogFiles() {
		if (logSizeAlearting()) {
			Log.w(TAG, "the log size is > 8M, try to free log files");
			freeOldFolders();
			Log.w(TAG, "old folders are deleted");
			if (logSizeAlearting()) {
				Log.w(TAG, "try to delete old log files");
				freeOldFiles();
			}
		}
	}

	private void startCleanUpTimer() {
		synchronized (this) {
			if (timer == null) {
				timer = new Timer();
				TimerTask task = new TimerTask() {
					public void run() {
						singleThreadService.execute(new Runnable() {
							public void run() {
								try {
									File file = getLogRoot();
									if ((file == null) || (!getLogRoot().exists())) {
										return;
									}
									spaceAvailable = freeSpace();
								} catch (NullPointerException e) {
									e.printStackTrace();
									Log.e(TAG, e.getMessage());
								} catch (Exception e) {
									e.printStackTrace();
									Log.e(TAG, e.getMessage());
								}
							}
						});
					}
				};
				timer.schedule(task, FREE_SPACE_TIMER, FREE_SPACE_TIMER);
			}
		}
	}
}
作者 east
Android 12月 11,2020

Android常用图片操作工具类


public final class ImageTools {

    /**
     * 将View转化为Bitmap
     *
     * @param view
     * @return
     */
    public static Bitmap convertViewToBitmap(View view) {
        view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        // view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        view.buildDrawingCache();
        Bitmap bitmap = view.getDrawingCache();
        return bitmap;
    }

    /**
     * 质量压缩法
     *
     * @param image
     * @return
     */
    public static Bitmap compressImage(Bitmap image) {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.PNG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
        int options = 100;
        while (baos.toByteArray().length / 1024 > 100) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩
            baos.reset();// 重置baos即清空baos
            options -= 10;// 每次都减少10
            if (options > 0 && options < 100)
                image.compress(Bitmap.CompressFormat.PNG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
        return bitmap;
    }

    /**
     * 图片按比例大小压缩方法(根据路径获取图片并压缩)
     *
     * @param srcPath
     * @return
     */
    public static Bitmap getimage(String srcPath) {
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        // 开始读入图片,此时把options.inJustDecodeBounds 设回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此时返回bm为空

        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        // 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
        float hh = 180f;// 这里设置高度为800f
        float ww = 500f;// 这里设置宽度为480f

        // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
        int be = 1;// be=1表示不缩放
        if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;// 设置缩放比例
        // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
        bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
        return bitmap;// compressImage(bitmap);// 压缩好比例大小后再进行质量压缩
    }

    /**
     * 图片按比例大小压缩方法(根据路径获取图片并压缩)
     *
     * @param srcPath
     * @return
     */
    public static Bitmap getImageFromSDCard(String srcPath) {
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        // 开始读入图片,此时把options.inJustDecodeBounds 设回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此时返回bm为空

        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        // 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
        float hh = 50f;// 这里设置高度为800f
        float ww = 50f;// 这里设置宽度为480f
        // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
        int be = 1;// be=1表示不缩放
        if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;// 设置缩放比例
        // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
        bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
        return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩
    }

    public static Bitmap getBitmapByPath(String srcPath) {
        return comp(BitmapFactory.decodeFile(srcPath));
    }

    /**
     * 图片按比例大小压缩方法(根据Bitmap图片压缩)
     *
     * @param image
     * @return
     */
    public static Bitmap comp(Bitmap image) {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.PNG, 100, baos);
        if (baos.toByteArray().length / 1024 > 1024) {// 判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
            baos.reset();// 重置baos即清空baos
            image.compress(Bitmap.CompressFormat.PNG, 50, baos);// 这里压缩50%,把压缩后的数据存放到baos中
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        // 开始读入图片,此时把options.inJustDecodeBounds 设回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        // 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
        float hh = 800f;// 这里设置高度为800f
        float ww = 480f;// 这里设置宽度为480f
        // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
        int be = 1;// be=1表示不缩放
        if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;// 设置缩放比例
        newOpts.inPreferredConfig = Config.RGB_565;// 降低图片从ARGB888到RGB565
        // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
        isBm = new ByteArrayInputStream(baos.toByteArray());
        bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        return bitmap;// 压缩好比例大小后再进行质量压缩
    }

    /**
     * Transfer drawable to bitmap
     *
     * @param drawable
     * @return
     */
    public static Bitmap drawableToBitmap(Drawable drawable) {
        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();

        Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
                : Bitmap.Config.RGB_565;
        Bitmap bitmap = Bitmap.createBitmap(w, h, config);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        drawable.draw(canvas);
        return bitmap;
    }

    /**
     * Bitmap to drawable
     *
     * @param bitmap
     * @return
     */
    public static Drawable bitmapToDrawable(Bitmap bitmap) {
        return new BitmapDrawable(bitmap);
    }

    /**
     * Input stream to bitmap
     *
     * @param inputStream
     * @return
     * @throws Exception
     */
    public static Bitmap inputStreamToBitmap(InputStream inputStream)
            throws Exception {
        return BitmapFactory.decodeStream(inputStream);
    }

    /**
     * Byte transfer to bitmap
     *
     * @param byteArray
     * @return
     */
    public static Bitmap byteToBitmap(byte[] byteArray) {
        if(byteArray!=null){
            if (byteArray.length != 0) {
                return BitmapFactory
                        .decodeByteArray(byteArray, 0, byteArray.length);
            } else {
                return null;
            }
        }else{
            return null;
        }

    }

    /**
     * Byte transfer to drawable
     *
     * @param byteArray
     * @return
     */
    public static Drawable byteToDrawable(byte[] byteArray) {
        ByteArrayInputStream ins = null;
        if (byteArray != null) {
            ins = new ByteArrayInputStream(byteArray);
        }
        return Drawable.createFromStream(ins, null);
    }

    /**
     * Bitmap transfer to bytes
     *
     * @return
     */
    public static byte[] bitmapToBytes(Bitmap bm) {
        byte[] bytes = null;
        if (bm != null) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
            bytes = baos.toByteArray();
        }
        return bytes;
    }

    /**
     * Drawable transfer to bytes
     *
     * @param drawable
     * @return
     */
    public static byte[] drawableToBytes(Drawable drawable) {
        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
        Bitmap bitmap = bitmapDrawable.getBitmap();
        byte[] bytes = bitmapToBytes(bitmap);
        ;
        return bytes;
    }

    /**
     * Base64 to byte[] //
     */
    // public static byte[] base64ToBytes(String base64) throws IOException {
    // byte[] bytes = Base64.decode(base64);
    // return bytes;
    // }
    //
    // /**
    // * Byte[] to base64
    // */
    // public static String bytesTobase64(byte[] bytes) {
    // String base64 = Base64.encode(bytes);
    // return base64;
    // }

    /**
     * Create reflection images
     *
     * @param bitmap
     * @return
     */
    public static Bitmap createReflectionImageWithOrigin(Bitmap bitmap) {
        final int reflectionGap = 4;
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();

        Matrix matrix = new Matrix();
        matrix.preScale(1, -1);

        Bitmap reflectionImage = Bitmap.createBitmap(bitmap, 0, h / 2, w,
                h / 2, matrix, false);

        Bitmap bitmapWithReflection = Bitmap.createBitmap(w, (h + h / 2),
                Config.ARGB_8888);

        Canvas canvas = new Canvas(bitmapWithReflection);
        canvas.drawBitmap(bitmap, 0, 0, null);
        Paint deafalutPaint = new Paint();
        canvas.drawRect(0, h, w, h + reflectionGap, deafalutPaint);

        canvas.drawBitmap(reflectionImage, 0, h + reflectionGap, null);

        Paint paint = new Paint();
        LinearGradient shader = new LinearGradient(0, bitmap.getHeight(), 0,
                bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff,
                0x00ffffff, TileMode.CLAMP);
        paint.setShader(shader);
        // Set the Transfer mode to be porter duff and destination in
        paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
        // Draw a rectangle using the paint with our linear gradient
        canvas.drawRect(0, h, w, bitmapWithReflection.getHeight()
                + reflectionGap, paint);

        return bitmapWithReflection;
    }

    /**
     * Get rounded corner images
     *
     * @param bitmap
     * @param roundPx
     *            5 10
     * @return
     */
    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
        Canvas canvas = new Canvas(output);
        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, w, h);
        final RectF rectF = new RectF(rect);
        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        return output;
    }

    /**
     * Resize the bitmap
     *
     * @param bitmap
     * @param width
     * @param height
     * @return
     */
    public static Bitmap zoomBitmap(Bitmap bitmap, int width, int height) {
        Bitmap newbmp = null;
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();

        // 960*1280

        if (w > 960 && h > 1280) {
            Matrix matrix = new Matrix();
            float scaleWidth = ((float) width / w);
            float scaleHeight = ((float) height / h);
            matrix.postScale(scaleWidth, scaleHeight);
            newbmp = Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
            bitmap.recycle();
        } else {
            newbmp = bitmap;
        }

        return newbmp;
    }

    /**
     * Resize the drawable
     *
     * @param drawable
     * @param w
     * @param h
     * @return
     */
    public static Drawable zoomDrawable(Drawable drawable, int w, int h) {
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        Bitmap oldbmp = drawableToBitmap(drawable);
        Matrix matrix = new Matrix();
        float sx = ((float) w / width);
        float sy = ((float) h / height);
        matrix.postScale(sx, sy);
        Bitmap newbmp = Bitmap.createBitmap(oldbmp, 0, 0, width, height,
                matrix, true);
        return new BitmapDrawable(newbmp);
    }

    /**
     * Get images from SD card by path and the name of image
     *
     * @param photoName
     * @return
     */
    public static Bitmap getPhotoFromSDCard(String path, String photoName) {
        Bitmap photoBitmap = BitmapFactory.decodeFile(path + "/" + photoName
                + ".png");
        if (photoBitmap == null) {
            return null;
        } else {
            return photoBitmap;
        }
    }

    public static Bitmap getPhotoFromSDCard(String path) {
        Bitmap photoBitmap = BitmapFactory.decodeFile(path);
        if (photoBitmap == null) {
            return null;
        } else {
            return photoBitmap;
        }
    }

    /**
     * Check the SD card
     *
     * @return
     */
    public static boolean checkSDCardAvailable() {
        return android.os.Environment.getExternalStorageState().equals(
                android.os.Environment.MEDIA_MOUNTED);
    }

    /**
     * Get image from SD card by path and the name of image
     *
     * @param path
     * @return
     */
    public static boolean findPhotoFromSDCard(String path, String photoName) {
        boolean flag = false;

        if (checkSDCardAvailable()) {
            File dir = new File(path);
            if (dir.exists()) {
                File folders = new File(path);
                File photoFile[] = folders.listFiles();
                for (int i = 0; i < photoFile.length; i++) {
                    String fileName = photoFile[i].getName().split("\\.")[0];
                    if (fileName.equals(photoName)) {
                        flag = true;
                    }
                }
            } else {
                flag = false;
            }
            // File file = new File(path + "/" + photoName + ".jpg" );
            // if (file.exists()) {
            // flag = true;
            // }else {
            // flag = false;
            // }

        } else {
            flag = false;
        }
        return flag;
    }

    /**
     * Save image to the SD card
     *
     * @param photoBitmap
     * @param photoName
     * @param path
     */
    public static void savePhotoToSDCard(Bitmap photoBitmap, String path,
                                         String photoName) {
        if (checkSDCardAvailable()) {
            File dir = new File(path);
            if (!dir.exists()) {
                dir.mkdirs();
            }

            File photoFile = new File(path, photoName);
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(photoFile);
                if (photoBitmap != null) {
                    if (photoBitmap.compress(Bitmap.CompressFormat.PNG, 100,
                            fileOutputStream)) {
                        fileOutputStream.flush();
                        // fileOutputStream.close();
                    }
                }
            } catch (FileNotFoundException e) {
                photoFile.delete();
                e.printStackTrace();
            } catch (IOException e) {
                photoFile.delete();
                e.printStackTrace();
            } finally {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Delete the image from SD card
     *
     * @param path
     *            file:///sdcard/temp.jpg
     */
    public static void deleteAllPhoto(String path) {
        if (checkSDCardAvailable()) {
            File folder = new File(path);
            File[] files = folder.listFiles();
            for (int i = 0; i < files.length; i++) {
                files[i].delete();
            }
        }
    }

    public static void deletePhotoAtPathAndName(String path, String fileName) {
        if (checkSDCardAvailable()) {
            File folder = new File(path);
            File[] files = folder.listFiles();
            for (int i = 0; i < files.length; i++) {
                LogUtil.d("libImageTools", files[i].getName());
                if (files[i].getName().equals(fileName)) {
                    files[i].delete();
                }
            }
        }
    }

    public static Bitmap getBitmap(String localStr){
        Bitmap photoBitmap = BitmapFactory.decodeFile(localStr);
        return photoBitmap;
    }

    public static Bitmap getImagesFromSDCard(String path, String photoName) {
        Bitmap photoBitmap = BitmapFactory.decodeFile(path.concat(photoName));
        if (photoBitmap == null) {
            return null;
        } else {
            return compressBitmap(path.concat(photoName));
        }
    }

    public static Bitmap compressBitmap(String photoPath) {
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        // 开始读入图片,此时把options.inJustDecodeBounds 设回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeFile(photoPath, newOpts);// 此时返回bm为空

        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        // 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
        float hh = 320f;// 这里设置高度为800f
        float ww = 240f;// 这里设置宽度为480f
        // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
        int be = 1;// be=1表示不缩放
        if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;// 2;// 设置缩放比例 width,hight设为原来的十分一
        // 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
        bitmap = BitmapFactory.decodeFile(photoPath, newOpts);

        // BitmapFactory.Options options = new BitmapFactory.Options();
        // options.inJustDecodeBounds = true;
        // Bitmap bitmap = BitmapFactory.decodeFile(photoPath, options);
        // //此时返回bm为空
        // options.inJustDecodeBounds = false;
        // //缩放比
        // int be = (int)(options.outHeight / (float)200);
        // if (be <= 0)
        // be = 1;
        // options.inSampleSize = be;
        // //重新读入图片,注意这次要把options.inJustDecodeBounds 设为 false哦
        // bitmap=BitmapFactory.decodeFile(photoPath,options);
        return bitmap;// compressImage(bitmap);

    }


    public static DisplayImageOptions getChatDisplayImageOptions() {
        return mChatDiaplayOptions;
    }

    static DisplayImageOptions mChatDiaplayOptions = new DisplayImageOptions.Builder()
            .cacheInMemory(true)// 设置下载的图片是否缓存在内存中
            .cacheOnDisk(true)// 设置下载的图片是否缓存在SD卡中
            .considerExifParams(true) // 是否考虑JPEG图像EXIF参数(旋转,翻转)
            .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)// 设置图片以如何的编码方式显示
            .bitmapConfig(Bitmap.Config.RGB_565)// 设置图片的解码类型//
            .displayer(new FadeInBitmapDisplayer(500))
            .build();

    public static DisplayImageOptions.Builder getChatDisplayImageOptionsBuilder() {
        return new DisplayImageOptions.Builder().cacheInMemory(true)// 设置下载的图片是否缓存在内存中
                .cacheOnDisk(true)// 设置下载的图片是否缓存在SD卡中
                .considerExifParams(true) // 是否考虑JPEG图像EXIF参数(旋转,翻转)
                .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)// 设置图片以如何的编码方式显示
                .bitmapConfig(Bitmap.Config.RGB_565)// 设置图片的解码类型//
                .displayer(new FadeInBitmapDisplayer(500));
    }
}
作者 east
Android 12月 11,2020

Android倒计时工具类

public abstract class TimeDownUtil {
    private final String TAG = getClass().getSimpleName();

    private long mCountDownInterval = 1000;
    private Timer mTimer;
    private TimerTask mTimerTask;

    /**
     * 时钟回调的次数
     */
    private int mCount = 0;

    /**
     * 倒计时时间,默认间隔单位为秒
     *
     * @param time 毫秒
     */
    public TimeDownUtil(final long time) {
        initTimer(time);
    }

    private void initTimer(long time) {
        mTimer = new Timer();
        mCount = (int) (time / mCountDownInterval);
        LogUtil.w(TAG, "count = " + mCount);
        mTimerTask = new TimerTask() {
            @Override
            public void run() {
                LogUtil.w(TAG, "mCount = " + mCount);
                if (mCount > 0) {
                    mCount--;
                    onTick(mCountDownInterval * mCount);
                } else {
                    onFinish();
                    cancel();
                }
            }
        };
    }

    /**
     * 倒计时时间,默认单位为秒
     *
     * @param time
     * @param countDownInterval 间隔时间,1000为1s
     */
    public TimeDownUtil(long time, long countDownInterval) {
        mCountDownInterval = countDownInterval;
        initTimer(time);
    }

    public void start() {
        mTimer.schedule(mTimerTask, 0, mCountDownInterval);
    }

    /**
     * Callback fired on regular interval.
     *
     * @param millisUntilFinished 距离结束还有多少时间
     */
    public abstract void onTick(long millisUntilFinished);

    /**
     * 倒计时结束的回调
     */
    public abstract void onFinish();

    public void cancel() {
        mTimer.cancel();
        mTimerTask.cancel();
    }


}
作者 east
Android 12月 11,2020

Android压缩解压工具


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class ZipUtil {
	private static final int BUFF_SIZE = 1024 * 1024; // 1M Byte
	
	/**
	 * 批量压缩文件(夹)
	 * 
	 * @param resFileList
	 *            要压缩的文件(夹)列表
	 * @param zipFile
	 *            生成的压缩文件
	 * @throws IOException
	 *             当压缩过程出错时抛出
	 */
	public static void zipFiles(Collection<File> resFileList, File zipFile)
			throws IOException {
		FileOutputStream fileOutputStream = new FileOutputStream(zipFile);
		BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
				fileOutputStream, BUFF_SIZE);
		ZipOutputStream zipout = new ZipOutputStream(bufferedOutputStream);

		for (File resFile : resFileList) {
			zipFile(resFile, zipout, "");
		}
		zipout.close();
	}

	/**
	 * 批量压缩文件(夹)
	 * 
	 * @param resFileList
	 *            要压缩的文件(夹)列表
	 * @param zipFile
	 *            生成的压缩文件
	 * @param comment
	 *            压缩文件的注释
	 * @throws IOException
	 *             当压缩过程出错时抛出
	 */
	public static void zipFiles(Collection<File> resFileList, File zipFile,
			String comment) throws IOException {
		ZipOutputStream zipout = new ZipOutputStream(new BufferedOutputStream(
				new FileOutputStream(zipFile), BUFF_SIZE));
		for (File resFile : resFileList) {
			zipFile(resFile, zipout, "");
		}
		zipout.setComment(comment);
		zipout.close();
	}

	/**
	 * 解压缩一个文件
	 * 
	 * @param zipFile
	 *            压缩文件
	 * @param folderPath
	 *            解压缩的目标目录
	 * @throws IOException
	 *             当解压缩过程出错时抛出
	 */
	public static void upZipFile(File zipFile, String folderPath)
			throws ZipException, IOException {
		File desDir = new File(folderPath);
		if (!desDir.exists()) {
			desDir.mkdirs();
		}
		
		ZipFile zf = new ZipFile(zipFile);
		for (Enumeration<?> entries = zf.entries(); entries.hasMoreElements();) {
			ZipEntry entry = ((ZipEntry) entries.nextElement());
			InputStream in = zf.getInputStream(entry);
			String str = folderPath + File.separator + entry.getName();
			str = new String(str.getBytes("8859_1"), "GB2312");
			File desFile = new File(str);
			if (!desFile.exists()) {
				File fileParentDir = desFile.getParentFile();
				if (!fileParentDir.exists()) {
					fileParentDir.mkdirs();
				}
				desFile.createNewFile();
			}
			OutputStream out = new FileOutputStream(desFile);
			byte buffer[] = new byte[BUFF_SIZE];
			int realLength;
			while ((realLength = in.read(buffer)) > 0) {
				out.write(buffer, 0, realLength);
			}
			in.close();
			out.close();
		}
	}

	/**
	 * 解压文件名包含传入文字的文件
	 * 
	 * @param zipFile
	 *            压缩文件
	 * @param folderPath
	 *            目标文件夹
	 * @param nameContains
	 *            传入的文件匹配名
	 * @throws ZipException
	 *             压缩格式有误时抛出
	 * @throws IOException
	 *             IO错误时抛出
	 */
	public static ArrayList<File> upZipSelectedFile(File zipFile,
			String folderPath, String nameContains) throws ZipException,
			IOException {
		ArrayList<File> fileList = new ArrayList<File>();

		File desDir = new File(folderPath);
		if (!desDir.exists()) {
			desDir.mkdir();
		}

		ZipFile zf = new ZipFile(zipFile);
		for (Enumeration<?> entries = zf.entries(); entries.hasMoreElements();) {
			ZipEntry entry = ((ZipEntry) entries.nextElement());
			if (entry.getName().contains(nameContains)) {
				InputStream in = zf.getInputStream(entry);
				String str = folderPath + File.separator + entry.getName();
				str = new String(str.getBytes("8859_1"), "GB2312");
				// str.getBytes("GB2312"),"8859_1" 输出
				// str.getBytes("8859_1"),"GB2312" 输入
				File desFile = new File(str);
				if (!desFile.exists()) {
					File fileParentDir = desFile.getParentFile();
					if (!fileParentDir.exists()) {
						fileParentDir.mkdirs();
					}
					desFile.createNewFile();
				}
				OutputStream out = new FileOutputStream(desFile);
				byte buffer[] = new byte[BUFF_SIZE];
				int realLength;
				while ((realLength = in.read(buffer)) > 0) {
					out.write(buffer, 0, realLength);
				}
				in.close();
				out.close();
				fileList.add(desFile);
			}
		}
		return fileList;
	}

	/**
	 * 获得压缩文件内文件列表
	 * 
	 * @param zipFile
	 *            压缩文件
	 * @return 压缩文件内文件名称
	 * @throws ZipException
	 *             压缩文件格式有误时抛出
	 * @throws IOException
	 *             当解压缩过程出错时抛出
	 */
	public static ArrayList<String> getEntriesNames(File zipFile)
			throws ZipException, IOException {
		ArrayList<String> entryNames = new ArrayList<String>();
		Enumeration<?> entries = getEntriesEnumeration(zipFile);
		while (entries.hasMoreElements()) {
			ZipEntry entry = ((ZipEntry) entries.nextElement());
			entryNames.add(new String(getEntryName(entry).getBytes("GB2312"),
					"8859_1"));
		}
		return entryNames;
	}

	/**
	 * 获得压缩文件内压缩文件对象以取得其属性
	 * 
	 * @param zipFile
	 *            压缩文件
	 * @return 返回一个压缩文件列表
	 * @throws ZipException
	 *             压缩文件格式有误时抛出
	 * @throws IOException
	 *             IO操作有误时抛出
	 */
	public static Enumeration<?> getEntriesEnumeration(File zipFile)
			throws ZipException, IOException {
		ZipFile zf = new ZipFile(zipFile);
		return zf.entries();

	}

	/**
	 * 取得压缩文件对象的注释
	 * 
	 * @param entry
	 *            压缩文件对象
	 * @return 压缩文件对象的注释
	 * @throws UnsupportedEncodingException
	 */
	public static String getEntryComment(ZipEntry entry)
			throws UnsupportedEncodingException {
		return new String(entry.getComment().getBytes("GB2312"), "8859_1");
	}

	/**
	 * 取得压缩文件对象的名称
	 * 
	 * @param entry
	 *            压缩文件对象
	 * @return 压缩文件对象的名称
	 * @throws UnsupportedEncodingException
	 */
	public static String getEntryName(ZipEntry entry)
			throws UnsupportedEncodingException {
		return new String(entry.getName().getBytes("GB2312"), "8859_1");
	}

	/**
	 * 压缩文件
	 * 
	 * @param resFile
	 *            需要压缩的文件(夹)
	 * @param zipout
	 *            压缩的目的文件
	 * @param rootpath
	 *            压缩的文件路径
	 * @throws FileNotFoundException
	 *             找不到文件时抛出
	 * @throws IOException
	 *             当压缩过程出错时抛出
	 */
	private static void zipFile(File resFile, ZipOutputStream zipout,
			String rootpath) throws FileNotFoundException, IOException {
		rootpath = rootpath
				+ (rootpath.trim().length() == 0 ? "" : File.separator)
				+ resFile.getName();
		rootpath = new String(rootpath.getBytes("8859_1"), "GB2312");
		if (resFile.isDirectory()) {
			File[] fileList = resFile.listFiles();
			for (File file : fileList) {
				zipFile(file, zipout, rootpath);
			}
		} else {
			byte buffer[] = new byte[BUFF_SIZE];
			BufferedInputStream in = new BufferedInputStream(
					new FileInputStream(resFile), BUFF_SIZE);
			ZipEntry zipEntry = new ZipEntry(rootpath);
			zipEntry.setTime(resFile.lastModified());
			zipout.putNextEntry(zipEntry);
			int realLength;
			while ((realLength = in.read(buffer)) != -1) {
				zipout.write(buffer, 0, realLength);
			}
			in.close();
			zipout.flush();
			zipout.closeEntry();
		}
	}
}
作者 east
Android 12月 11,2020

Android高精确度的四则运算工具类


import java.math.BigDecimal;

/**
* 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精
* 确的浮点数运算,包括加减乘除和四舍五入。
*/
public class Arith { //默认除法运算精度
private static final int DEF_DIV_SCALE = 10; //这个类不能实例化

private Arith() {
}

/**
* 提供精确的加法运算。
*
* @param v1 被加数
* @param v2 加数
* @return 两个参数的和
*/
public static double add(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}

public static String add(String v1, String v2){
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.add(b2).stripTrailingZeros().toPlainString();
}

/**
* 提供精确的减法运算。
*
* @param v1 被减数
* @param v2 减数
* @return 两个参数的差
*/
public static double sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}

public static String sub(String v1, String v2){
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.subtract(b2).stripTrailingZeros().toPlainString();
}

/**
* 提供精确的乘法运算。
*
* @param v1 被乘数
* @param v2 乘数
* @return 两个参数的积
*/
public static double mul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}

public static String mul(String v1, String v2){
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.multiply(b2).stripTrailingZeros().toPlainString();
}

/**
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
* 小数点以后10位,以后的数字四舍五入。
*
* @param v1 被除数
* @param v2 除数
* @return 两个参数的商
*/
public static double div(double v1, double v2) {
return div(v1, v2, DEF_DIV_SCALE);
}

/**
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
* 定精度,以后的数字四舍五入。
*
* @param v1 被除数
* @param v2 除数
* @param scale 表示表示需要精确到小数点以后几位。
* @return 两个参数的商
*/
public static double div(double v1, double v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}


public static String div(String v1, String v2, int scale){
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString();
}

/**
* 提供精确的小数位四舍五入处理。
*
* @param v 需要四舍五入的数字
* @param scale 小数点后保留几位
* @return 四舍五入后的结果
*/
public static double round(double v, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
};
作者 east
Android 12月 11,2020

Android通用菜单项

public class MenuItemView extends RelativeLayout{
private TextView mTextView;
private TextView mLeftTextView;
private TextView mRightTextView;
private ImageView mImageView;
private ImageView mArrowIcon;
private CharSequence mTitle;
private CharSequence mRightText;
private int mTextColor;
private int mTextSize;
private int mIconResId;
private boolean mArrowVisibility;

public MenuItemView(Context context) {
    super(context);
    inflate(context, R.layout.widget_menu_item, this);
    initView();
}

public MenuItemView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public MenuItemView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    inflate(context, R.layout.widget_menu_item, this);

    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MenuItemView, defStyle, 0);
    mTitle = typedArray.getText(R.styleable.MenuItemView_text);
    mRightText = typedArray.getText(R.styleable.MenuItemView_rightText);
    mTextColor = typedArray.getColor(R.styleable.MenuItemView_textColor, 0);
    mTextSize = typedArray.getDimensionPixelSize(R.styleable.MenuItemView_textSize, 0);
    mIconResId = typedArray.getResourceId(R.styleable.MenuItemView_imageSrc, 0);
    mArrowVisibility = typedArray.getBoolean(R.styleable.MenuItemView_arrowVibility, true);
    typedArray.recycle();

    initView();
}

public void setArrowVisibility(int visibility) {
    mArrowIcon.setVisibility(visibility);
}

private void initView() {
    mTextView = (TextView) findViewById(R.id.menu_item_tv_title);
    mLeftTextView = (TextView) findViewById(R.id.menu_item_tv_left);
    mRightTextView = (TextView) findViewById(R.id.menu_item_tv_right);
    mImageView = (ImageView) findViewById(R.id.menu_item_iv_icon);
    mArrowIcon = (ImageView) findViewById(R.id.menu_item_iv_arrow);
    //标题
    mTextView.setText(mTitle);

    if (mIconResId != 0) {
        mImageView.setImageResource(mIconResId);
    }
    if (mTextColor != 0) {
        mTextView.setTextColor(mTextColor);
    }
    if (mTextSize != 0) {
        mTextView.setTextSize(mTextSize);
    }
    if (!mArrowVisibility) {
        mArrowIcon.setVisibility(View.GONE);
    }

    //右边文字
    if (mRightText != null) {
        mRightTextView.setVisibility(View.VISIBLE);
        mRightTextView.setText(mRightText);
    }
    this.setClickable(true);
}


public void setText(String text) {
    mTextView.setText(text);
}

public void setLeftText(String text) {
    //左边文字
    mLeftTextView.setVisibility(View.VISIBLE);
    mLeftTextView.setText(text);
}

public void setRightText(int resId) {
    //右边文字
    mRightTextView.setVisibility(View.VISIBLE);
    mRightTextView.setText(resId);
}

public void setRightText(String text) {
    //右边文字
    mRightTextView.setVisibility(View.VISIBLE);
    mRightTextView.setText(text);
}

public void setRightTextColor(int resId) {
    //右边文字
    mRightTextView.setVisibility(View.VISIBLE);
    mRightTextView.setTextColor(resId);
}


public void setRightTextVisibility(int visibility) {
    mRightTextView.setVisibility(visibility);
}

public void hideLeftText() {
    mLeftTextView.setVisibility(View.GONE);
}

}

<?xml version="1.0" encoding="utf-8"?><!-- 菜单item -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:paddingBottom="10dp"
    android:paddingTop="10dp">

    <ImageView
        android:id="@+id/menu_item_iv_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true" />

    <TextView
        android:id="@+id/menu_item_tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="@dimen/content_margin"
        android:layout_toRightOf="@+id/menu_item_iv_icon"
        android:textColor="@color/text_black"
        android:textSize="@dimen/text_size_4x" />


    <TextView
        android:id="@+id/menu_item_tv_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@+id/menu_item_tv_title"
        android:textColor="@color/text_black"
        android:textSize="16sp"
        android:visibility="gone" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:gravity="center_vertical">

        <TextView
            android:id="@+id/menu_item_tv_right"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:textColor="@color/text_black"
            android:textSize="16sp"
            android:visibility="gone" />

        <ImageView
            android:id="@+id/menu_item_iv_arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:src="@drawable/home_point_icon" />
    </LinearLayout>
</RelativeLayout>
作者 east
Android 12月 11,2020

Android自定义带未读信息红点TextView

public class RedPointTextView extends TextView {
private static final int DEFAULT_MIN_SIZE_DIP = 18; //最小尺寸
private static final int DEFAULT_TB_PADDING_DIP = 3; //上下边距
private static final int DEFAULT_LR_PADDING_DIP = 6; //左右边距
private static final int DEFAULT_TEXT_SIZE = 11; //dp
private static final int DEFAULT_TEXT_COLOR = Color.WHITE;
private int mLeftPadding;
private int mTopPadding;
private int mMinSize;

public RedPointTextView(Context context) {
    super(context);
    init();
}

public RedPointTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

public RedPointTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

private void init() {
    mLeftPadding = dipToPixels(DEFAULT_LR_PADDING_DIP);
    mTopPadding = dipToPixels(DEFAULT_TB_PADDING_DIP);
    mMinSize = dipToPixels(DEFAULT_MIN_SIZE_DIP);

    setIncludeFontPadding(false);
    setGravity(Gravity.CENTER);
    setMinWidth(mMinSize);
    setMinHeight(mMinSize);
    setTextColor(DEFAULT_TEXT_COLOR);
    setTextSize(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_TEXT_SIZE);
    setBackgroundResource(R.drawable.shape_oval_pink);
    setVisibility(View.GONE);
}

private int dipToPixels(int dip) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}

public void setUnreadCount(int count) {
    if (count <= 0) {
        setVisibility(GONE);
    } else {
        setVisibility(GONE);
        if (count < 10) {
            setPadding(0, 0, 0, 0);
        } else {
            setPadding(mLeftPadding, mTopPadding, mLeftPadding, mTopPadding);
        }
        if (count > 99) {
            setText("99+");
        } else {
            setText(String.valueOf(count));
        }
    }
}

}

作者 east
Android 12月 11,2020

Android自定义富文本控件

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcel;
import android.support.annotation.IntDef;
import android.support.annotation.IntRange;
import android.support.annotation.Nullable;
import android.text.Html;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.method.Touch;
import android.text.style.ClickableSpan;
import android.text.style.ImageSpan;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import pl.droidsonroids.gif.GifDrawable;

public class RichTextView extends GifSpanTextView implements Drawable.Callback, View.OnAttachStateChangeListener {

private static Pattern IMAGE_TAG_PATTERN = Pattern.compile("\\<img(.*?)\\>");
private static Pattern IMAGE_WIDTH_PATTERN = Pattern.compile("width=\"(.*?)\"");
private static Pattern IMAGE_HEIGHT_PATTERN = Pattern.compile("height=\"(.*?)\"");
private static Pattern IMAGE_SRC_PATTERN = Pattern.compile("src=\"(.*?)\"");
private static Pattern IMAGE_BIG_SRC_PATTERN = Pattern.compile("bigsrc=\"(.*?)\"");
private static Pattern IMAGE_HREF_PATTERN = Pattern.compile("href=\"(.*?)\"");

private Drawable placeHolder, errorImage;//占位图,错误图
private OnImageClickListener onImageClickListener;//图片点击回调
private OnURLClickListener onURLClickListener;//超链接点击回调
//    private HashSet<Target> targets;

// private HashSet targets;
private HashMap mImages;
private HashMap mBigImages;
private ImageFixListener mImageFixListener;
private int d_w = 200;
private int d_h = 100;
private boolean isScroll = true;

private String imageHost;
private boolean dontConsumeNonUrlClicks = true;
private boolean linkHit;
private Context mContext;
private GlideImageGeter glideImageGeter;

public RichTextView(Context context) {
    this(context, null);
}

public RichTextView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public RichTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    init(context, attrs);
}

private void init(Context context, AttributeSet attrs) {
    mContext = context;
 //   targets = new HashSet<>();
    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RichTextView);
    placeHolder = typedArray.getDrawable(R.styleable.RichTextView_placeHolder);
    errorImage = typedArray.getDrawable(R.styleable.RichTextView_errorImage);

    d_w = typedArray.getDimensionPixelSize(R.styleable.RichTextView_defaultWidth, Utils.Dp2Px(context, d_w));
    d_h = typedArray.getDimensionPixelSize(R.styleable.RichTextView_defaultHeight, Utils.Dp2Px(context, d_h));

    isScroll = typedArray.getBoolean(R.styleable.RichTextView_isScroll, isScroll);

    if (placeHolder == null) {
        placeHolder = new ColorDrawable(Color.GRAY);
    }
    placeHolder.setBounds(0, 0, d_w, d_h);
    if (errorImage == null) {
        errorImage = new ColorDrawable(Color.GRAY);
    }
    errorImage.setBounds(0, 0, d_w, d_h);
    typedArray.recycle();
}


/**
 * 设置富文本
 *
 * @param text 富文本
 */
public void setRichText(String text) {
    try {
        glideImageGeter = new GlideImageGeter(getContext(), this);
     //   targets.clear();
        matchImages(text);

        if (text != null) {
            text = text.replaceAll(" ", "\t");  //替换空格
        }
        Spanned spanned = Html.fromHtml(text, glideImageGeter, null);
        SpannableStringBuilder spannableStringBuilder;
        if (spanned instanceof SpannableStringBuilder) {
            spannableStringBuilder = (SpannableStringBuilder) spanned;
        } else {
            spannableStringBuilder = new SpannableStringBuilder(spanned);
        }

        // 处理图片得点击事件
        ImageSpan[] imageSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), ImageSpan.class);
        final List<String> imageUrls = new ArrayList<>();

        for (int i = 0, size = imageSpans.length; i < size; i++) {
            ImageSpan imageSpan = imageSpans[i];
            String imageUrl = imageSpan.getSource();
            String bigImageUrl = mBigImages.get(imageUrl);
            if (!TextUtils.isEmpty(bigImageUrl)) {
                imageUrl = bigImageUrl;
            }
            int start = spannableStringBuilder.getSpanStart(imageSpan);
            int end = spannableStringBuilder.getSpanEnd(imageSpan);
            imageUrl = imageUrl.startsWith("http") ? imageUrl : imageHost + imageUrl;
            if (imageUrl.endsWith(".gif") || imageUrl.endsWith(".bmp") || imageUrl.endsWith(".jpg") || imageUrl.endsWith(".jpeg") || imageUrl.endsWith(".JPG") || imageUrl.endsWith(".JPEG") || imageUrl.endsWith(".png") || imageUrl.endsWith(".PNG")) {
                imageUrls.add(imageUrl);
            }
            final int finalI = i;
            ClickableSpan clickableSpan = new ClickableSpan() {
                @Override
                public void onClick(View widget) {
                    if (onImageClickListener != null) {
                        onImageClickListener.imageClicked(imageUrls, finalI);
                    }
                }
            };
            ClickableSpan[] clickableSpans = spannableStringBuilder.getSpans(start, end, ClickableSpan.class);
            if (clickableSpans != null && clickableSpans.length != 0) {
                for (ClickableSpan cs : clickableSpans) {
                    spannableStringBuilder.removeSpan(cs);
                }
            }
            spannableStringBuilder.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        // 处理超链接点击事件
        URLSpan[] urlSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), URLSpan.class);

        for (int i = 0, size = urlSpans == null ? 0 : urlSpans.length; i < size; i++) {
            URLSpan urlSpan = urlSpans[i];

            int start = spannableStringBuilder.getSpanStart(urlSpan);
            int end = spannableStringBuilder.getSpanEnd(urlSpan);

            spannableStringBuilder.removeSpan(urlSpan);
            spannableStringBuilder.setSpan(new CallableURLSpan(urlSpan.getURL(), onURLClickListener), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        //表情转义
        final Matcher matcher = EmojiHelper.getInstance().getEmojiMatcher(spanned.toString());

        while (matcher.find()) {
            final String name = matcher.group(1);

            final GifDrawable drawable = EmojiHelper.getInstance().getGifDrawable(name);
            if (drawable != null) {
                final ImageSpan gifSpan = new GifImageSpan(drawable);
                spannableStringBuilder.setSpan(gifSpan, matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }

        super.setText(spanned);
        if (isScroll) {
            setMovementMethod(LocalLinkMovementMethod.getInstance());    //解决ListView里TextView设置LinkMovementMethod后导致其ItemClick失效的问题
        }
        setLongClickable(false);    //屏蔽长按事件,防止部分手机Spannable长按时崩溃
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

/*
private void addTarget(ImageTarget target) {
targets.add(target);
}
/ /*
* 从文本中拿到标签,并获取图片url和宽高
*/
private void matchImages(String text) {
mImages = new HashMap<>();
mBigImages = new HashMap<>();
ImageHolder holder;
Matcher imageMatcher, srcMatcher, bigSrcMatcher, widthMatcher, heightMatcher, hrefMatcher;
int position = 0;
hrefMatcher = IMAGE_HREF_PATTERN.matcher(text);
String hrefImageUrl = null;
if (hrefMatcher.find()) {
hrefImageUrl = getTextBetweenQuotation(hrefMatcher.group().trim().substring(4));
}
imageMatcher = IMAGE_TAG_PATTERN.matcher(text);
while (imageMatcher.find()) {
String image = imageMatcher.group().trim();
srcMatcher = IMAGE_SRC_PATTERN.matcher(image);
String src = null;
if (srcMatcher.find()) {
src = getTextBetweenQuotation(srcMatcher.group().trim().substring(4));
}
if (TextUtils.isEmpty(src)) {
continue;
}
bigSrcMatcher = IMAGE_BIG_SRC_PATTERN.matcher(image);
String bigSrc = null;
if (bigSrcMatcher.find()) {
bigSrc = getTextBetweenQuotation(bigSrcMatcher.group().trim().substring(4));
}
if (TextUtils.isEmpty(bigSrc)) {
if (!TextUtils.isEmpty(hrefImageUrl)) {
bigSrc = hrefImageUrl;
} else {
bigSrc = src;
}
}

        holder = new ImageHolder(src, bigSrc, position);
        widthMatcher = IMAGE_WIDTH_PATTERN.matcher(image);
        if (widthMatcher.find()) {
            holder.width = parseStringToInteger(getTextBetweenQuotation(widthMatcher.group().trim().substring(6)));
        }

        heightMatcher = IMAGE_HEIGHT_PATTERN.matcher(image);
        if (heightMatcher.find()) {
            holder.height = parseStringToInteger(getTextBetweenQuotation(heightMatcher.group().trim().substring(6)));
        }

        mImages.put(holder.src, holder);
        mBigImages.put(holder.src, holder.bigSrc);
        position++;
    }
}

private int parseStringToInteger(String integerStr) {
    int result = -1;
    if (!TextUtils.isEmpty(integerStr)) {
        try {
            result = Integer.parseInt(integerStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return result;
}

/**
 * 从双引号之间取出字符串
 */
@Nullable
private static String getTextBetweenQuotation(String text) {
    Pattern pattern = Pattern.compile("\"(.*?)\"");
    Matcher matcher = pattern.matcher(text);
    if (matcher.find()) {
        return matcher.group(1);
    }
    return null;
}

  


@Override
public boolean onTouchEvent(MotionEvent event) {
linkHit = false;
boolean res = super.onTouchEvent(event);

    if (dontConsumeNonUrlClicks)
        return linkHit;
    return res;

}

public static class LocalLinkMovementMethod extends LinkMovementMethod {
    static LocalLinkMovementMethod sInstance;


    public static LocalLinkMovementMethod getInstance() {
        if (sInstance == null)
            sInstance = new LocalLinkMovementMethod();

        return sInstance;
    }


    @Override
    public boolean onTouchEvent(TextView widget,
                                Spannable buffer, MotionEvent event) {
        int action = event.getAction();

        if (action == MotionEvent.ACTION_UP ||
                action == MotionEvent.ACTION_DOWN) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            x -= widget.getTotalPaddingLeft();
            y -= widget.getTotalPaddingTop();

            x += widget.getScrollX();
            y += widget.getScrollY();

            Layout layout = widget.getLayout();
            int line = layout.getLineForVertical(y);
            int off = layout.getOffsetForHorizontal(line, x);

            ClickableSpan[] link = buffer.getSpans(
                    off, off, ClickableSpan.class);

            if (link.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    link[0].onClick(widget);
                } else if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer,
                            buffer.getSpanStart(link[0]),
                            buffer.getSpanEnd(link[0]));
                }

                if (widget instanceof RichTextView) {
                    ((RichTextView) widget).linkHit = true;
                }
                return true;

            } else {
                Selection.removeSelection(buffer);
                Touch.onTouchEvent(widget, buffer, event);
                return false;
            }
        }
        return Touch.onTouchEvent(widget, buffer, event);
    }
}


private static final class URLDrawable extends Drawable {
    private Drawable drawable;

    @Override
    public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {

    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {

    }

    @Override
    public int getOpacity() {
        return 0;
    }

    @Override
    public void draw(Canvas canvas) {
        if (drawable != null)
            drawable.draw(canvas);
    }

    public void setDrawable(Drawable drawable) {
        this.drawable = drawable;
    }
}

private static class CallableURLSpan extends URLSpan {

    private OnURLClickListener onURLClickListener;

    public CallableURLSpan(String url, OnURLClickListener onURLClickListener) {
        super(url);
        this.onURLClickListener = onURLClickListener;
    }

    @SuppressWarnings("unused")
    public CallableURLSpan(Parcel src, OnURLClickListener onURLClickListener) {
        super(src);
        this.onURLClickListener = onURLClickListener;
    }

    @Override
    public void onClick(View widget) {
        if (onURLClickListener != null && onURLClickListener.urlClicked(getURL())) {
            return;
        }
        super.onClick(widget);
    }
}

public static class ImageHolder {
    public static final int DEFAULT = 0;
    public static final int CENTER_CROP = 1;
    public static final int CENTER_INSIDE = 2;

    @IntDef({DEFAULT, CENTER_CROP, CENTER_INSIDE})
    public @interface ScaleType {
    }

    private final String src;
    private final String bigSrc;
    private final int position;
    private int width = -1, height = -1;
    private int scaleType = DEFAULT;

    public ImageHolder(String src, String bigSrc, int position) {
        this.src = src;
        this.bigSrc = bigSrc;
        this.position = position;
    }

    public String getSrcUrl() {
        return src;
    }

    public String getBigSrcUrl() {
        return bigSrc;
    }

    public int getHeight() {
        return height;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    @ScaleType
    public int getScaleType() {
        return scaleType;
    }

    public void setScaleType(@ScaleType int scaleType) {
        this.scaleType = scaleType;
    }
}

@SuppressWarnings("unused")
public ImageHolder getImageHolder(String url) {
    return mImages.get(url);
}

@SuppressWarnings("unused")
public void setPlaceHolder(Drawable placeHolder) {
    this.placeHolder = placeHolder;
    this.placeHolder.setBounds(0, 0, d_w, d_h);
}

@SuppressWarnings("unused")
public void setErrorImage(Drawable errorImage) {
    this.errorImage = errorImage;
    this.errorImage.setBounds(0, 0, d_w, d_h);
}

public void setOnImageClickListener(OnImageClickListener onImageClickListener) {
    this.onImageClickListener = onImageClickListener;
}

public void setImageFixListener(ImageFixListener mImageFixListener) {
    this.mImageFixListener = mImageFixListener;
}

/**
 * 设置超链接点击回调事件(需在setRichText方法之前调用)
 *
 * @param onURLClickListener 回调
 */
public void setOnURLClickListener(OnURLClickListener onURLClickListener) {
    this.onURLClickListener = onURLClickListener;
}

@Override
public void onViewAttachedToWindow(View v) {

}

@Override
public void onViewDetachedFromWindow(View v) {
    glideImageGeter.recycle();
}

public interface OnImageClickListener {
    /**
     * 图片被点击后的回调方法
     *
     * @param imageUrls 本篇富文本内容里的全部图片
     * @param position  点击处图片在imageUrls中的位置
     */
    void imageClicked(List<String> imageUrls, int position);
}

public interface OnURLClickListener {

    /**
     * 超链接点击得回调方法
     *
     * @param url 点击得url
     * @return true:已处理,false:未处理(会进行默认处理)
     */
    boolean urlClicked(String url);
}

public interface ImageFixListener {
    /**
     * 修复图片尺寸的方法
     *
     * @param holder ImageHolder对象
     */
    void onFix(ImageHolder holder);

}

public void configure(String imageHostPrefix) {
    imageHost = imageHostPrefix;
}

}

作者 east
Android 12月 11,2020

Android身份证合法性校验工具类



import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
* <p>
* 身份证合法性校验
* </p>
*
* <pre>
* --15位身份证号码:第7、8位为出生年份(两位数),第9、10位为出生月份,第11、12位代表出生日期,第15位代表性别,奇数为男,偶数为女。
* --18位身份证号码:第7、8、9、10位为出生年份(四位数),第11、第12位为出生月份,第13、14位代表出生日期,第17位代表性别,奇数为男,偶数为女。
* 最后一位为校验位
* </pre>
*
*
@author
*/

public class IdcardValidator {
/**
* <pre>
* 省、直辖市代码表:
* 11 : 北京 12 : 天津 13 : 河北 14 : 山西 15 : 内蒙古
* 21 : 辽宁 22 : 吉林 23 : 黑龙江 31 : 上海 32 : 江苏
* 33 : 浙江 34 : 安徽 35 : 福建 36 : 江西 37 : 山东
* 41 : 河南 42 : 湖北 43 : 湖南 44 : 广东 45 : 广西 46 : 海南
* 50 : 重庆 51 : 四川 52 : 贵州 53 : 云南 54 : 西藏
* 61 : 陕西 62 : 甘肃 63 : 青海 64 : 宁夏 65 : 新疆
* 71 : 台湾
* 81 : 香港 82 : 澳门
* 91 : 国外
* </pre>
*/
private static String cityCode[] = { "11", "12", "13", "14", "15", "21",
"22", "23", "31", "32", "33", "34", "35", "36", "37", "41", "42",
"43", "44", "45", "46", "50", "51", "52", "53", "54", "61", "62",
"63", "64", "65", "71", "81", "82", "91" };

/**
* 每位加权因子
*/
private static int power[] = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5,
8, 4, 2 };

/**
* 验证所有的身份证的合法性
*
*
@param idcard
* 身份证
*
@return 合法返回true,否则返回false
*/
public static boolean isValidatedAllIdcard(String idcard) {
if (idcard == null || "".equals(idcard)) {
return false;
}
if (idcard.length() == 15) {
return validate15IDCard(idcard);
}
return validate18Idcard(idcard);
}

/**
* <p>
* 判断18位身份证的合法性
* </p>
* 根据〖中华人民共和国国家标准GB11643-1999〗中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。
* 排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
* <p>
* 顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同 日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配 给女性。
* </p>
* <p>
* 1.前1、2位数字表示:所在省份的代码; 2.第3、4位数字表示:所在城市的代码; 3.第5、6位数字表示:所在区县的代码;
* 4.第7~14位数字表示:出生年、月、日; 5.第15、16位数字表示:所在地的派出所的代码;
* 6.第17位数字表示性别:奇数表示男性,偶数表示女性;
* 7.第18位数字是校检码:也有的说是个人信息码,一般是随计算机的随机产生,用来检验身份证的正确性。校检码可以是0~9的数字,有时也用x表示。
* </p>
* <p>
* 第十八位数字(校验码)的计算方法为: 1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4
* 2 1 6 3 7 9 10 5 8 4 2
* </p>
* <p>
* 2.将这17位数字和系数相乘的结果相加。
* </p>
* <p>
* 3.用加出来和除以11,看余数是多少
* </p>
* 4.余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3
* 2。
* <p>
* 5.通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。
* </p>
*
*
@param idcard
* @return
*/
public static boolean validate18Idcard(String idcard) {
if (idcard == null) {
return false;
}

// 非18位为假
if (idcard.length() != 18) {
return false;
}
// 获取前17位
String idcard17 = idcard.substring(0, 17);

// 前17位全部为数字
if (!isDigital(idcard17)) {
return false;
}

String provinceid = idcard.substring(0, 2);
// 校验省份
if (!checkProvinceid(provinceid)) {
return false;
}

// 校验出生日期
String birthday = idcard.substring(6, 14);

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");

try {
Date birthDate = sdf.parse(birthday);
String tmpDate = sdf.format(birthDate);
if (!tmpDate.equals(birthday)) {// 出生年月日不正确
return false;
}

} catch (ParseException e1) {

return false;
}

// 获取第18位
String idcard18Code = idcard.substring(17, 18);

char c[] = idcard17.toCharArray();

int bit[] = converCharToInt(c);

int sum17 = 0;

sum17 = getPowerSum(bit);

// 将和值与11取模得到余数进行校验码判断
String checkCode = getCheckCodeBySum(sum17);
if (null == checkCode) {
return false;
}
// 将身份证的第18位与算出来的校码进行匹配,不相等就为假
if (!idcard18Code.equalsIgnoreCase(checkCode)) {
return false;
}

return true;
}

/**
* 校验15位身份证
*
* <pre>
* 只校验省份和出生年月日
* </pre>
*
*
@param idcard
* @return
*/
public static boolean validate15IDCard(String idcard) {
if (idcard == null) {
return false;
}
// 非15位为假
if (idcard.length() != 15) {
return false;
}

// 15全部为数字
if (!isDigital(idcard)) {
return false;
}

String provinceid = idcard.substring(0, 2);
// 校验省份
if (!checkProvinceid(provinceid)) {
return false;
}

String birthday = idcard.substring(6, 12);

SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");

try {
Date birthDate = sdf.parse(birthday);
String tmpDate = sdf.format(birthDate);
if (!tmpDate.equals(birthday)) {// 身份证日期错误
return false;
}

} catch (ParseException e1) {

return false;
}

return true;
}

/**
* 将15位的身份证转成18位身份证
*
*
@param idcard
* @return
*/
public static String convertIdcarBy15bit(String idcard) {
if (idcard == null) {
return null;
}

// 非15位身份证
if (idcard.length() != 15) {
return null;
}

// 15全部为数字
if (!isDigital(idcard)) {
return null;
}

String provinceid = idcard.substring(0, 2);
// 校验省份
if (!checkProvinceid(provinceid)) {
return null;
}

String birthday = idcard.substring(6, 12);

SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");

Date birthdate = null;
try {
birthdate = sdf.parse(birthday);
String tmpDate = sdf.format(birthdate);
if (!tmpDate.equals(birthday)) {// 身份证日期错误
return null;
}

} catch (ParseException e1) {
return null;
}

Calendar cday = Calendar.getInstance();
cday.setTime(birthdate);
String year = String.valueOf(cday.get(Calendar.YEAR));

String idcard17 = idcard.substring(0, 6) + year + idcard.substring(8);

char c[] = idcard17.toCharArray();
String checkCode = "";

// 将字符数组转为整型数组
int bit[] = converCharToInt(c);

int sum17 = 0;
sum17 = getPowerSum(bit);

// 获取和值与11取模得到余数进行校验码
checkCode = getCheckCodeBySum(sum17);

// 获取不到校验位
if (null == checkCode) {
return null;
}
// 将前17位与第18位校验码拼接
idcard17 += checkCode;
return idcard17;
}

/**
* 校验省份
*
*
@param provinceid
* @return 合法返回TRUE,否则返回FALSE
*/
private static boolean checkProvinceid(String provinceid) {
for (String id : cityCode) {
if (id.equals(provinceid)) {
return true;
}
}
return false;
}

/**
* 数字验证
*
*
@param str
* @return
*/
private static boolean isDigital(String str) {
return str.matches("^[0-9]*$");
}

/**
* 将身份证的每位和对应位的加权因子相乘之后,再得到和值
*
*
@param bit
* @return
*/
private static int getPowerSum(int[] bit) {

int sum = 0;

if (power.length != bit.length) {
return sum;
}

for (int i = 0; i < bit.length; i++) {
for (int j = 0; j < power.length; j++) {
if (i == j) {
sum = sum + bit[i] * power[j];
}
}
}
return sum;
}

/**
* 将和值与11取模得到余数进行校验码判断
*
*
*
@param sum17
* @return 校验位
*/
private static String getCheckCodeBySum(int sum17) {
String checkCode = null;
switch (sum17 % 11) {
case 10:
checkCode = "2";
break;
case 9:
checkCode = "3";
break;
case 8:
checkCode = "4";
break;
case 7:
checkCode = "5";
break;
case 6:
checkCode = "6";
break;
case 5:
checkCode = "7";
break;
case 4:
checkCode = "8";
break;
case 3:
checkCode = "9";
break;
case 2:
checkCode = "x";
break;
case 1:
checkCode = "0";
break;
case 0:
checkCode = "1";
break;
}
return checkCode;
}

/**
* 将字符数组转为整型数组
*
*
@param c
* @return
* @throws NumberFormatException
*/
private static int[] converCharToInt(char[] c) throws NumberFormatException {
int[] a = new int[c.length];
int k = 0;
for (char temp : c) {
a[k++] = Integer.parseInt(String.valueOf(temp));
}
return a;
}

}
作者 east

1 2 3 下一个

关注公众号“大模型全栈程序员”回复“小程序”获取1000个小程序打包源码。回复”chatgpt”获取免注册可用chatgpt。回复“大数据”获取多本大数据电子书

标签

AIGC AI创作 bert chatgpt github GPT-3 gpt3 GTP-3 hive mysql O2O tensorflow UI控件 不含后台 交流 共享经济 出行 图像 地图定位 外卖 多媒体 娱乐 小程序 布局 带后台完整项目 开源项目 搜索 支付 效率 教育 日历 机器学习 深度学习 物流 用户系统 电商 画图 画布(canvas) 社交 签到 联网 读书 资讯 阅读 预订

官方QQ群

小程序开发群:74052405

大数据开发群: 952493060

近期文章

  • 详解Python当中的pip常用命令
  • AUTOSAR如何在多个供应商交付的配置中避免ARXML不兼容?
  • C++thread pool(线程池)设计应关注哪些扩展性问题?
  • 各类MCAL(Microcontroller Abstraction Layer)如何与AUTOSAR工具链解耦?
  • 如何设计AUTOSAR中的“域控制器”以支持未来扩展?
  • C++ 中避免悬挂引用的企业策略有哪些?
  • 嵌入式电机:如何在低速和高负载状态下保持FOC(Field-Oriented Control)算法的电流控制稳定?
  • C++如何在插件式架构中使用反射实现模块隔离?
  • C++如何追踪内存泄漏(valgrind/ASan等)并定位到业务代码?
  • C++大型系统中如何组织头文件和依赖树?

文章归档

  • 2025年6月
  • 2025年5月
  • 2025年4月
  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年1月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年7月
  • 2018年6月

分类目录

  • Android (73)
  • bug清单 (79)
  • C++ (34)
  • Fuchsia (15)
  • php (4)
  • python (43)
  • sklearn (1)
  • 云计算 (20)
  • 人工智能 (61)
    • chatgpt (21)
      • 提示词 (6)
    • Keras (1)
    • Tensorflow (3)
    • 大模型 (1)
    • 智能体 (4)
    • 深度学习 (14)
  • 储能 (44)
  • 前端 (4)
  • 大数据开发 (488)
    • CDH (6)
    • datax (4)
    • doris (30)
    • Elasticsearch (15)
    • Flink (78)
    • flume (7)
    • Hadoop (19)
    • Hbase (23)
    • Hive (40)
    • Impala (2)
    • Java (71)
    • Kafka (10)
    • neo4j (5)
    • shardingsphere (6)
    • solr (5)
    • Spark (99)
    • spring (11)
    • 数据仓库 (9)
    • 数据挖掘 (7)
    • 海豚调度器 (10)
    • 运维 (34)
      • Docker (3)
  • 小游戏代码 (1)
  • 小程序代码 (139)
    • O2O (16)
    • UI控件 (5)
    • 互联网类 (23)
    • 企业类 (6)
    • 地图定位 (9)
    • 多媒体 (6)
    • 工具类 (25)
    • 电商类 (22)
    • 社交 (7)
    • 行业软件 (7)
    • 资讯读书 (11)
  • 嵌入式 (70)
    • autosar (63)
    • RTOS (1)
    • 总线 (1)
  • 开发博客 (16)
    • Harmony (9)
  • 技术架构 (6)
  • 数据库 (32)
    • mongodb (1)
    • mysql (13)
    • pgsql (2)
    • redis (1)
    • tdengine (4)
  • 未分类 (6)
  • 程序员网赚 (20)
    • 广告联盟 (3)
    • 私域流量 (5)
    • 自媒体 (5)
  • 量化投资 (4)
  • 面试 (14)

功能

  • 登录
  • 文章RSS
  • 评论RSS
  • WordPress.org

All Rights Reserved by Gitweixin.本站收集网友上传代码, 如有侵犯版权,请发邮件联系yiyuyos@gmail.com删除.