diff --git a/app/src/main/java/cc/niushuai/dididone/ui/calendar/CalendarFragment.java b/app/src/main/java/cc/niushuai/dididone/ui/calendar/CalendarFragment.java index 251b73f..1c091ed 100644 --- a/app/src/main/java/cc/niushuai/dididone/ui/calendar/CalendarFragment.java +++ b/app/src/main/java/cc/niushuai/dididone/ui/calendar/CalendarFragment.java @@ -19,7 +19,6 @@ import com.mikepenz.fontawesome_typeface_library.FontAwesome; import com.mikepenz.iconics.IconicsDrawable; import com.mikepenz.iconics.view.IconicsImageView; import com.necer.calendar.BaseCalendar; -import com.necer.entity.CalendarDate; import com.necer.entity.Lunar; import com.necer.enumeration.DateChangeBehavior; import com.necer.listener.OnCalendarChangedListener; @@ -31,7 +30,9 @@ import org.joda.time.LocalDate; import java.util.List; import cc.niushuai.dididone.R; +import cc.niushuai.dididone.biz.entity.Record; import cc.niushuai.dididone.databinding.FragmentCalendarBinding; +import cc.niushuai.dididone.util.IntSnowflake; import cn.hutool.core.date.DateUtil; public class CalendarFragment extends Fragment { @@ -87,7 +88,7 @@ public class CalendarFragment extends Fragment { String lunarDate = lunar.chineseEra + "年" + lunar.lunarMonthStr + lunar.lunarDayStr; // 当前选中的日期放到最上面 - add2Top(localDate.toString(), lunarDate); + add2Top(new Record(), localDate.toString(), lunarDate); // 重置上次选中日期 lastDate = localDate.toString(); @@ -96,24 +97,23 @@ public class CalendarFragment extends Fragment { } - private void add2Top(String localDate, String lunarDate) { + private void add2Top(Record record, String localDate, String lunarDate) { // 外层layout 白色背景 存在边距 RelativeLayout itemLayout = new RelativeLayout(getContext()); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 175); layoutParams.setMargins(25, 20, 25, 15); - itemLayout.setId(13); + itemLayout.setId(IntSnowflake.next_id()); itemLayout.setLayoutParams(layoutParams); itemLayout.setPadding(15, 15, 15, 5); itemLayout.setBackgroundResource(R.drawable.bg_miui10); // itemLayout.setOrientation(LinearLayout.VERTICAL); - // projectIcon layout RelativeLayout.LayoutParams iconLayout = new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); - iconLayout.setMargins(10,5,0,0); + iconLayout.setMargins(10, 5, 0, 0); IconicsImageView iconView = new IconicsImageView(getContext()); - iconView.setId(111); + iconView.setId(IntSnowflake.next_id()); iconView.setLayoutParams(iconLayout); IconicsDrawable icon = new IconicsDrawable(getContext()); icon.icon(FontAwesome.Icon.faw_swimmer).color(Color.BLUE).sizeDp(50); @@ -122,16 +122,16 @@ public class CalendarFragment extends Fragment { itemLayout.addView(iconView); RelativeLayout.LayoutParams projectLayout = new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - projectLayout.setMargins(20,5,0,0); + projectLayout.setMargins(20, 5, 0, 0); projectLayout.addRule(RelativeLayout.RIGHT_OF, 111); - TextView projectTextView = this.getTextView(222, localDate, projectLayout, R.color._333333, 16, Gravity.CENTER_VERTICAL); + TextView projectTextView = this.getTextView(IntSnowflake.next_id(), localDate, projectLayout, R.color._333333, 16, Gravity.CENTER_VERTICAL); itemLayout.addView(projectTextView); RelativeLayout.LayoutParams dateLayout = new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - dateLayout.setMargins(20,25,0,0); + dateLayout.setMargins(20, 25, 0, 0); dateLayout.addRule(RelativeLayout.RIGHT_OF, 111); dateLayout.addRule(RelativeLayout.BELOW, 222); - TextView dateTextView = this.getTextView(333, DateUtil.now(), dateLayout, R.color._666666, 12, Gravity.TOP); + TextView dateTextView = this.getTextView(IntSnowflake.next_id(), DateUtil.now(), dateLayout, R.color._666666, 12, Gravity.TOP); itemLayout.addView(dateTextView); binding.cCalendarCheckInList.removeAllViews(); diff --git a/app/src/main/java/cc/niushuai/dididone/util/IntSnowflake.java b/app/src/main/java/cc/niushuai/dididone/util/IntSnowflake.java new file mode 100644 index 0000000..df4252f --- /dev/null +++ b/app/src/main/java/cc/niushuai/dididone/util/IntSnowflake.java @@ -0,0 +1,216 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cc.niushuai.dididone.util; + +import java.net.NetworkInterface; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +/** + * @author funkye + * @author selfishlover + */ +public class IntSnowflake { + private static final IntSnowflake snowflake = new IntSnowflake(0L); + + public static int next_id() { + return (int) snowflake.nextId(); + } + + public static String next_id_str() { + return snowflake.nextId() + ""; + } + + /** + * Start time cut (2020-05-03) + */ + private final long twepoch = 1588435200000L; + + /** + * The number of bits occupied by workerId + */ + private final int workerIdBits = 8; + + /** + * The number of bits occupied by timestamp + */ + private final int timestampBits = 12; + + /** + * The number of bits occupied by sequence + */ + private final int sequenceBits = 12; + + /** + * Maximum supported machine id, the result is 1023 + */ + private final int maxWorkerId = ~(-1 << workerIdBits); + /** + * mask that help to extract timestamp and sequence from a long + */ + private final long timestampAndSequenceMask = ~(-1L << (timestampBits + sequenceBits)); + /** + * business meaning: machine ID (0 ~ 1023) + * actual layout in memory: + * highest 1 bit: 0 + * middle 10 bit: workerId + * lowest 53 bit: all 0 + */ + private long workerId; + /** + * timestamp and sequence mix in one Long + * highest 11 bit: not used + * middle 41 bit: timestamp + * lowest 12 bit: sequence + */ + private AtomicLong timestampAndSequence; + + /** + * instantiate an IdWorker using given workerId + * + * @param workerId if null, then will auto assign one + */ + public IntSnowflake(Long workerId) { + initTimestampAndSequence(); + initWorkerId(workerId); + } + + /** + * init first timestamp and sequence immediately + */ + protected void initTimestampAndSequence() { + long timestamp = getNewestTimestamp(); + long timestampWithSequence = timestamp << sequenceBits; + this.timestampAndSequence = new AtomicLong(timestampWithSequence); + } + + /** + * init workerId + * + * @param workerId if null, then auto generate one + */ + protected void initWorkerId(Long workerId) { + if (workerId == null) { + workerId = generateWorkerId(); + } + if (workerId > maxWorkerId || workerId < 0) { + String message = String.format("worker Id can't be greater than %d or less than 0", maxWorkerId); + throw new IllegalArgumentException(message); + } + this.workerId = workerId << (timestampBits + sequenceBits); + } + + /** + * get next UUID(base on snowflake algorithm), which look like: + * highest 1 bit: always 0 + * next 10 bit: workerId + * next 41 bit: timestamp + * lowest 12 bit: sequence + * + * @return UUID + */ + public long nextId() { + waitIfNecessary(); + long next = timestampAndSequence.incrementAndGet(); + long timestampWithSequence = next & timestampAndSequenceMask; + return workerId | timestampWithSequence; + } + + /** + * block current thread if the QPS of acquiring UUID is too high + * that current sequence space is exhausted + */ + private void waitIfNecessary() { + long currentWithSequence = timestampAndSequence.get(); + long current = currentWithSequence >>> sequenceBits; + long newest = getNewestTimestamp(); + if (current >= newest) { + try { + Thread.sleep(5); + } catch (InterruptedException ignore) { + // don't care + } + } + } + + /** + * get newest timestamp relative to twepoch + */ + private long getNewestTimestamp() { + return System.currentTimeMillis() - twepoch; + } + + /** + * auto generate workerId, try using mac first, if failed, then randomly generate one + * + * @return workerId + */ + private long generateWorkerId() { + try { + return generateWorkerIdBaseOnMac(); + } catch (Exception e) { + return generateRandomWorkerId(); + } + } + + /** + * use lowest 10 bit of available MAC as workerId + * + * @return workerId + * @throws Exception when there is no available mac found + */ + private long generateWorkerIdBaseOnMac() throws Exception { + Enumeration all = NetworkInterface.getNetworkInterfaces(); + while (all.hasMoreElements()) { + NetworkInterface networkInterface = all.nextElement(); + boolean isLoopback = networkInterface.isLoopback(); + boolean isVirtual = networkInterface.isVirtual(); + if (isLoopback || isVirtual) { + continue; + } + byte[] mac = networkInterface.getHardwareAddress(); + return ((mac[4] & 0B11) << 8) | (mac[5] & 0xFF); + } + throw new RuntimeException("no available mac found"); + } + + /** + * randomly generate one as workerId + * + * @return workerId + */ + private long generateRandomWorkerId() { + return new Random().nextInt(maxWorkerId + 1); + } + + + public static void main(String[] args) { + + Map map = new HashMap<>(); + for (int i = 0; i < 100000000; i++) { + long id = next_id(); + map.put((int) id, (map.getOrDefault(id, 0) + 1)); + } + List collect = map.values().stream().filter(item -> item > 0).collect(Collectors.toList()); + System.out.println(map.size()); + System.out.println(collect); + } +} \ No newline at end of file